Besides the new interfaces for threads and atomic operations that I already mentioned earlier, others of the new features that come with C11 are in reality already present in many compilers. Only that not all of them might agree upon the syntax, and especially not with the new syntax of C11. So actually emulating some these features is already possible and I implemented some of them in P99 on the base of what gcc provides: these are
static_assert
(or_Static_assert
) to make compile time assertions (a misnomer, again!)alignof
(or_Alignof
) to get the alignment constraint for a typealignas
(of_Alignas
) to constraint the alignment of objects orstruct
members. Only the variant that receives a constant expression is directly supported. The variant with a type argument can be obtained simply byalignas(alignof(T))
.noreturn
(or_Noreturn
) to specify that a function is not expected to return to the callerthread_local
(or_Thread_local
) for thread local storage_Generic
for type generic expression.
The most interesting among them is probably the latter feature, type generic expressions.
Gcc already has three builtins that can be used to something like _Generic
:
__typeof__(EXP)
gives the type of the expressionEXP
__builtin_types_compatible_p(T1, T2)
is true if the two types are compatible__builtin_choose_expr(CNTRL, EXP1, EXP2)
choose between the two expressions at compile time.
This only gives binary decisions and not multiple ones like _Generic
but with a good dose of P99_FOR
we can come to something that resembles a lot. (I spare you the details of the implementation.) The syntax that we support is as follows
#include "p99_generic.h" #define P99_GENERIC(EXP, DEF, ...) <some nasty use of P99 and gcc builtins>
For example as in the following
P99_GENERIC(a, , // empty default expression (int*, a), (double*, x));
That is an expression EXP
, followed by a default value DEF
, followed by a list of type value pairs. So here this is an expression that depending on the type of a will have a type of int*
or double*
that will be set to a
or x
, respectively.
In C11 syntax, the above would be coded with some kind of “label” syntax:
_Generic(a, int*: a, double*: x);
As you can see above, the default value can be omitted. If so, it is replaced with some appropriate expression that should usually give you a syntax error.
Here is an example with a default expression that will be used when none of the types matches:
uintmax_t max_uintmax(uintmax_t, uintmax_t); int max_int(int, int); long max_long(long, long); long long max_llong(long long, long long); float max_float(float, float); double max_double(double, double); a = P99_GENERIC(a + b, max_uintmax, (int, max_int), (long, max_long), (long long, max_llong), (float, max_float), (double, max_double))(a, b);
In C11 syntax
a = _Generic(a + b, default: max_uintmax, int: max_int, long: max_long, long long: max_llong, float: max_float, double: max_double)(a, b);
Here all the expressions evaluate to a function specifier. If a + b
is int
, … or double
the appropriate maximum function is chosen for that type. If none of these matches, the one for uintmax_t
is chosen. The corresponding function is then evaluated with a
and b
as arguments.
- Because the choice expression is
a + b
its type is the promoted common type ofa
andb
. E.g for all types that are narrower thanint
, e.gshort
, normally int will be the type of the expression andmax_int
will be the function. Ifa
would beunsigned
andb
would bedouble
the result would bedouble
as well. -
The return type of the
_Generic
expression is a function to two arguments. If it would be forint
, e.g, the type would beint ()(int, int)
. So the return type of the function call would beint
in that case. - The arguments are promoted and converted to the expected type of the chosen function.
NB: if the compiler is already C11 complying, the P99_GENERIC
expression will just be translated to the corresponding _Generic
expression.
Otherwise only gcc and compatible compilers are supported.
Addendum: Today I checked out the new version of clang and discovered that their new version 3.1 that came out in December already supports _Generic
directly. Well done.
Just wanted to let you know
P99_GENERIC
rocks.Just one thing, it would be great if the default value would allow something to abort compiling, like
currently I use
assert("fail")
as default which is of incorrect type, as it returnsvoid
, same behaviour, but feels wrong.Anyway, great to have this.
You probably mean something like
static_assert
since this would be a compile time error, no?The idea of
_Generic
as by C11 is that there is automatically an error, if no default is given and none of the choices triggers. This should be the case here, too, since then you have an empty expression that messes up the syntax completely.To improve the error message one could in effect add a
_Pragma
. Something likeUnfortunately giving diagnostics through pragmas is not standard, this version here would work for gcc and Co. But other compilers should at least give you a diagnostic for an unknown
#pragma
.Actually a solution with
_Pragma
will not work, because this always gives the diagnostic, regardless of the actual choice expression. But I found another solution that is a bit more sophisticated and uses a gcc extension, namely error attributes to functions. (Using gcc features is not a restriction since we are talking of the mapping ofP99_GENERIC
to gcc, anyhow.)I will push a new release with that solution later today.
I created a snippet to demonstrate my problem with default values.
For me, if there is no default value, it does not work at all, even matching generics end up being NULL instead.
Now,
0x7f70f1658c48 (nil) & 0x7f70f1658c48 0x7fff2394d620
is not what I expected, but maybe it is my expectations?Actually I’d expect this to fail during compiling, as you said the default is empty, and nothing matches, so fail?
0x7f70f1658c48 (nil)
is wrong to me as the generic for fB should have matched,0x7f70f1658c48 0x7fff2394d620, &A
matchesstruct msg B *
?It works when I use a default value, static_asserts do not work here, assert does, as the return value mismatches and can’t be casted properly.
new version works fine, thanks for taking care.
Thanks to you for testing and giving feedback. Glad that this works, now.
For the values that you saw printed. These were the “correct” ones. You didn’t initialize your variables. The corresponding members that you printed were just uninitialized pointers. If I initialize the variables correctly (
P99_INIT
comes handy) it prints(nil) (nil)
for the first print.Jens
PS: please use
[sourcecode][/sourcecode]
tags when you include larger code snipsets in a commentI saw I was missing proper initialization, still the p99-2012-01-28 version was somewhat inconsistent:
I did not expect
to compile, and the result returned by it was somewhat off my expectations too.
Looking on the cpp results
I think the zero default value for 2012-01-28 was wrong, but well, it is fixed for me in 2012-01-30, which can be seen here:
While the error returned by gcc does not match the information cpp has
it’s enough to see something is wrong.
Thanks again.
Ah, not to forget, if you want to use the snippet for a regression test, it is all yours.
– I would have used the tags, but I was unable to find a link for the syntax – maybe I would see a syntax menu bar if I had js on?
Yes, probably was too naive.
It is very difficult to force gcc to spit out useful error diagnostics for all the cases. After all, this is just an emulation of the feature.
What you see is the fallback diagnostic when
P99_GENERIC
is used as a statement or full expression. If it is used as part of another expression, you’d basically see what you have seen through the preprocessor.The diagnostic when it is used in a global declaration context is even worse. It just tells that there is a function call where a compile time expression is expected. I guess we have to live with that for the time being.
No, in the menu you just have simple editing features, but not that one. This is one of the features of wordpress that you’d have to know about.
Jens