P99
|
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... | |
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
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
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:
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
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 >
FLT_EVAL_METHOD
is 2 (all floating point operations are performed in long double) cast the expression down to double
#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