13. Language extensions¶
The Calypsi C compiler supports several C language extensions that can be convenient. However, consider their portability implications.
13.1. Overloaded functions¶
The overloaded functions support provides overloading of functions in
C similar to C++. Overloading in C is introduced using the
overloadable attribute. For example, you might provide several
overloaded versions of a sine function that invokes the appropriate
standard function computing the sine of a value with float,
double, or long double precision:
#include <math.h>
float __attribute__((overloadable)) sine(float x) { return sinf(x); }
double __attribute__((overloadable)) sine(double x) { return sin(x); }
long double __attribute__((overloadable)) sine(long double x) { return sinl(x); }
The compiler calls the most suitable function based on argument types. Overloaded functions are name mangled as C uses a single global namespace.
13.2. Statement expressions¶
This GCC extension allows a statement to appear where an expression is expected. Use it to allow loops, switches, and local variables within an expression.
A compound statement is a sequence of statements enclosed by braces. In the example below, parentheses around the braces create a statement expression:
({ int y = foo (); int z;
if (y > 0) z = y;
else z = - y;
z; })
The last statement in a statement expression is an expression followed
by a semicolon. In this case, the variable z is used as the
result of the entire statement expression.
Safe macros¶
Statement expressions can also implement safe macros that evaluate their parameters only once.
Recall the well known max macro:
#define max(a,b) ((a) > (b) ? (a) : (b))
When used, this expands the largest value twice, which may be undesirable if it has side effects or represents an expensive computation.
With a statement expression this can be expressed as:
#define maxint(a,b) \
({int _a = (a), _b = (b); _a > _b ? _a : _b; })
While this solves the double-evaluation issue, it introduces other
subtle problems. First, you need to specify the type (see also
__auto_type extension below), and it may cause variable shadowing.
In most cases, inline functions offer a better alternative to statement expressions. Inline functions avoid subtle variable shadowing problems, result in more readable code, and generate equally efficient code when inlined. The minor caveat is that inline functions may not be inlined, while statement expressions are always expanded in place.
13.3. Auto type¶
The auto type extension allows specifying a type that is automatically
selected, using the __auto_type keyword. The declaration must
declare only one variable, whose declarator must be an identifier.
The declaration must be initialized, and the variable type is
determined by the initializer type.
Using __auto_type, the “max” macro in the previous section can be written to select the type automatically:
#define max(a,b) \
({__auto_type _a = (a); \
__auto_type _b = (b); \
_a > _b ? _a : _b; })
13.4. Typeof¶
Refer to the type of an expression using __typeof. The syntax
resembles sizeof, but semantically, it acts like a typename defined
with typedef.
Similar to sizeof, there are two ways to write the argument to
__typeof: with an expression or with a type. For example, with an
expression:
__typeof (x)
This creates a type identical to the identifier x in the current
context.
You can also use a typename as an argument:
__typeof (int *)
In this case, the type is simply a pointer to int.
Typeof can also be used to implement the “max” macro:
#define max(a,b) \
({__typeof (a) _a = (a); \
__typeof (b) _b = (b); \
_a > _b ? _a : _b; })
13.5. Generics¶
C11 style _Generic is now supported. This allows expressions to
expand to different outcomes based on a controlling expression. For
example:
extern void stringFunc(char*);
extern void otherStringFunc(char*);
extern void string4Func(char[4]);
#define F(X) _Generic(&(X), \
default: otherStringFunc, \
char**: stringFunc, \
char(*)[4]: string4Func, \
char const**: stringFunc, \
char const(*)[4]: string4Func \
)(X)
void foo(char *p) {
F(p);
F("foo");
F("longer string");
}
The macro F calls different functions based on its input. For
example, a string literal "foo" (3 characters plus null terminator)
is treated as a char array of 4 bytes, leading to a call to
string4Func(). The p parameter, an ordinary char*,
results in a call to stringFunc(). Finally, "longer string" is
treated as a char array with a length other than four, matching the
default: alternative and resulting in a call to
otherStringFunc().