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().