Compile-time conditionals and unique constants using C11 _Generic
About a month ago, I started playing around with the new C standard,
namely C11, and its new features. While I have always been fond of
some of them, such as explicit alignment support, others never really
caught my interest. One of those not-so-interesting additions was the
new _Generic construct, which allows rather limited ad-hoc
compile-time type-based dispatching. While I reckon it was necessary
in order to support the <tgmath.h> header in a meaningful way, as
well as, perhaps, the growing number of integral types, I’d never
seen it as anything more than that: an implementor’s device given
standard blessing.
Well, that was until a few days ago. While experimenting with a macro
of mine, I tried to improve it by using C11 features, and, in doing
so, stumbled upon a rather less boring use of _Generic: compile-time
"type-level" constants, and conditional compilation based on it!
(Well, maybe it sounds obvious to some of you; for the rest, please
read on!)
The basics of _Generic constants
Usually, symbolic constants are some arbitrary literal cast to an appropriate type, like this:
#define MY_MAGIC_PONY ((pony_t)-1)
This works for integer and floating point values, but is not
guaranteed by the standard to work for pointers (except for 0, which
yields a null pointer constant). You might want to try anyway, or you
may take the address of some private dummy object instead.
#define MY_MAGIC_PONY (&my_magic_pony_)
extern struct pony my_magic_pony_;
Well, the bad news is that if you need that constant at run time, there’s not much I can do for you; you’ll have to keep using that. On the bright side, if the constant is only needed at compile time (e.g. if it is always generated by some macro, to induce a specific path in a conditional), there may be hope still.
With _Generic, you can now specify conditions based on types, which
means, you can use dummy types instead of dummy values for your
symbolic compile-time-only constants:
#define MY_MAGIC_PONY ((struct dummy_magic_pony *)NULL)
struct dummy_magic_pony;
#define MY_MAGIC_MACRO(..., p, ...) \
... _Generic((p), \
struct dummy_magic_pony *: ..., \
default: ...) ...
OK, so what does this bring us? Well, three things:
- We don’t need to create random objects for pointer constants.
- We don’t need to set aside a special value.
- And we don’t need to rely on the compiler eliminating spurious dead branches everywhere because of macro expansion.
Sample application 1: default arguments
So, we’ve seen how it’s done; sounds trivial, looks trivial, but what can we use it for? If this were just for me, I’d say "it comes in handy when you write complex macros sometimes", but I tried to come up with a simple enough example for this short article: default arguments. In case you may be wondering, though, this is for demonstration purposes only; don’t try this at home… well, what I mean is that probably nobody is going to use this, which is totally fine.
Anyway, while it is well-known(?) that default arguments were possible in C99 using preprocessor hacks, all solutions I’ve seen were rather cumbersome to implement (you’re welcome to prove me wrong; I haven’t tried too hard to look for the nicest way).
Probably the most usable solution comes with free keyword arguments (called named parameters, in some languages, as well) as a bonus. Just use a structure as your last parameter and stuff it with a compound literal. The disadvantage is that your defaults are all zeros (or whatever default initializer you get for the field type)… unless you name all of your arguments and include defaults in the compound literal; but that’s not what we’re aiming for here: we’re looking for plain old optional positional parameters.
You could also count the number of arguments in the variable argument list of a macro, but this requires a lot of boilerplate to define a version for each possible number of arguments.
Lastly, you could use arbitrary symbols and test for equality using preprocessor logic (for those who don’t know, it is possible with the standard C99 preprocessor to macro-branch on whether a given argument is equal to a few different things… it just requires quite a lot of work in the background and has some limitations as well — read the sources of any heavyweight preprocessor library and you’ll see; e.g. the one provided with COS). However, this has some quirks (which are outside of the scope of this article) and is definitely not for the casual preprocessor user. But the overall design is rather appealing, so we’ll keep with that except…
We can now use _Generic type constants instead of symbols. Let’s
look at some code:
/* Generic definitions, for all functions thereafter. */
struct dflarg_;
#define DFLARG ((struct dflarg_ *)NULL)
#define IFDFL(arg, dflval) \
_Generic((arg), \
struct dflarg_ *: dflval, \
default: (arg))
/*
* Some function foobar with two optional arguments.
* We take the second (non-optional) argument as part of
* __VA_ARGS__ so the list is never empty.
*/
int foobar(int, int, int, int);
#define foobar(x, ...) \
foobar_((x), __VA_ARGS__, DFLARG, DFLARG)
#define foobar_(x, y, z, t, ...) \
(foobar)((x), (y), IFDFL((z), 42), IFDFL((t), 36))
And here you go! Default arguments in C11 using _Generic.
Sample application 2: argument list length check
In our previous example, you’ve probably noticed that foobar now
accepts extra arguments, which is not exactly ideal. More generally,
it happens quite often with variadic macros (in my experience,
especially "helper" macros that tend to be buried under more
user-friendly ones, and that may need to handle the results of
concatenating, splitting, mapping, and other operations on previous
argument lists) that there’s a limit to how many arguments you
actually want to handle.
Again, it is possible with the C99 preprocessor to check for the argument list length, but this typically involves even more heavy preprocessor logic than before, as you’ll want to do arithmetic with the preprocessor. There are ways to avoid the arithmetics, but as far as I know, they all require quite a bit of preprocessor metaprogramming in their own right.
With C11 and _Generic, however, you could just use a type-level
constant… how? This is really just more of the above (well, it is
the same trick, after all):
/* Our end-of-list marker is really just another default. */
#define EOARGS DFLARG
#define ISEOARGS(arg) \
_Generic((arg), \
struct dflarg_ *: true, \
default: false)
#define foobar(x, ...) \
foobar_((x), __VA_ARGS__, DFLARG, DFLARG, EOARGS)
#define foobar_(x, y, z, t, eoargs, ...) \
(_Static_assert(ISEOARGS(eoargs), \
"too many arguments to foobar"), \
(foobar)((x), (y), IFDFL((z), 42), IFDFL((t), 36)))
If you try this code… it should fail. That is because
_Static_assert is a declaration according to the C11 grammar. This
can be worked around by wrapping the _Static_assert in an inline
structure declaration within a compound literal, like so:
#define inline_static_assert(x, s) \
((void)(const struct { int _unused; \
_Static_assert((x), s); }){ 0 })
Unfortunately, this (supposedly corrected) code does not compile
with Clang 3.0. Otherwise, we’d get the following final macro for
foobar_:
#define foobar_(x, y, z, t, eoargs, ...) \
(inline_static_assert(ISEOARGS(eoargs), \
"too many arguments to foobar"), \
(foobar)((x), (y), IFDFL((z), 42), IFDFL((t), 36)))
I only have the N1570 draft so I can’t say for sure that this is
correct, but my copy specifically states that a _Static_assert can
occur inside a structure declaration (§ 6.7.2.1).
Hence we’re left with emulating _Static_assert, with the traditional
negative array size trick, for example:
#define inline_static_assert(x, s) \
((void)(char * [(x) ? 1 : -1]){ s })
Now, the following call will fail to compile, although with an ugly message — can’t have it all, I guess:
foobar(6, 7, 1+1, 3*4, 5/5);
Conclusion
Well, this is it. _Generic is not widely supported yet, but
hopefully support will come soon enough (and in the meantime, you can
use P99 if you’re so inclined), at least for people living outside
of the C-hater kingdom that Visual Studio has become. And once it
does, I think it will help with simplifying some preprocessor hacks
that used to require a lot of added ma^Wlogic in order to branch at
macro-expansion time.
![[Atom]](feed.png)