P99
Modules | Macros
+ Collaboration diagram for Generic type expressions:

Modules

 Generic identification of families of types or values
 
 determine type related properties
 
 type generic printing
 
 Compile time constant expressions
 

Macros

#define P99_GENERIC(...)
 Type generic expression in anticipation of C11 _Generic. More...
 
#define P99_GENERIC_LIT(...)
 For each generic choice return a compound literal of the chosen type. More...
 
#define P99_GENERIC_SIZE(UI, ...)
 Similar to P99_GENERIC but the choice is not according to the type of the expression UI but to its unsigned value. More...
 
#define P99_GENERIC_SIZE_LIT(UI, ...)
 Similar to P99_GENERIC_SIZE but returns a compound literal of the chosen type. More...
 
#define P99_IN_RANGE(R, S, L)   P00_IN_RANGE((R), (S), (L), P00_IN_RANGE_LIST())
 check if R is in the range [S, S + L) More...
 
#define P99_TYPED_TERN(COND, YES, NO)
 A compile time ternary operator that is analogous to COND ? YES : NO that keeps the type of the chosen expression. More...
 

Detailed Description

C11 provides a new feature to write template-like expressions with the macro preprocessor, _Generic. Here we provide some tools that emulate this feature by means of gcc specific extensions.

_Generic is one single construct which could be seen as an analogue to a switch statement, only that the different choices are made with matching types and not values. A typical example is

#define sin(X) _Generic((X), default: sin, float: sinf, long double: sinld)(X)

In contrast to that single construct, in C++ there are a several of constructs that would be needed to implement similar functionalities: function overloading, templates, constexpr.

Type generic expressions have an important new feature that was difficult to implement before C11:

C++ has a construct that decides on the type of an expression, but which is less powerful: function overloading. The actual function that is called at any point depends on the types of its arguments. E.g in

b = abs(a);

the return type of the function call would depend on the type of a, for that simple example it should be the same type as a.

Such simple type generic functions can be implemented through macros in C11 without problems. They have the advantage that the result must not necessarily be a function call but can be any type of expression, in particular constants. This can be convenient in a context for which optimization is crucial, either for CPU efficiency or size of the code. To enforce similar optimizations as with C11 _Generic, you'd have to use the new constexpr feature in C++ in the declaration of the functions that is used.

But _Generic is more powerful than that. It also can take such "code" branches according to a value, examples:

#define TOTO_APPROX(X) P99_TYPED_TERN(sizeof(toto) > 4, toto_ld(X), toto_d(X))

Using _Generic underneath P99_TYPED_TERN, this defines a function-like macro TOTO_APPROX that chooses between to functions according to the size of a type toto. The result type is the type of any of the branches that is chosen. The two types need not be compatible.

The choice expression (sizeof ...) is not known during preprocessing phases, so an if/#else preprocessor conditional could not be used for the same purpose.On the other hand a conventional ternary expression

#define TOTO_APPROX0(X) (sizeof(toto) > 4 ? toto_ld(X) : toto_d(X))

would impose that the return types of the two functions would have to be compatible (both arithmetic types or both pointer types, e.g). If both were arithmetic types the result of the whole would be the wider of the two types. If e.g toto_ld would return long double and toto_d only double, in any case the result of the whole would still be long double,

In C++, one would need a template class that would be parametrized with a bool to obtain the same effect.

< should I leave this in? sounds a bit complicated >

#define FLOATING_EVAL(X) P99_GENERIC_SIZE(10+FLT_EVAL_METHOD, (X), (12, (double)(X)))
printf("toto is %f\n", FLOATING_EVAL(x * y))

This would ensure that the printf call would never see a long double, even if the arguments are only float or double.

See also
P99_GENERIC