Saturday, 21 May 2011

You Looking At Me? T-Shirt

static_assert and the C++ Preprocessor

With a title like that, you know you're in for a bit of geekiness.

With C++0x we now have proper static asserts. Hurrah! Except I don't like them. Their syntax is:

static_assert(sizeof(int) < 5, "Integers too big");

To me, the second argument is extra fluff that should be optional, but isn't. Perhaps we can get around it with preprocessor macros:

STATIC_ASSERT(sizeof(int) < 5, "Integers too big");
STATIC_ASSERT(sizeof(int) < 5);

But how do we overload preprocessor macros? With the help of variadic macros it is (just about) possible to overload based on the number of arguments. Ironically, this is much easier with the C99 preprocessor than with the current C++ one; all the solutions for C++ that I've seen need tweaking for different compilers. The code below works for Microsoft C++ 2010.

As an aside, Jens Gustedt has produced an amazing (and instructive) set of C99 preprocessor macros called P99. Well worth a look.

But for our C++ case, we want to construct something along the lines of:

#define STATIC_ASSERT(condition, ...) \
    if (__VA_ARGS__ is empty) then \
        static_assert(condition, #condition) \
    else \
        static_assert(condition, __VA_ARGS__)

There are two problems here: (a) how to determine if there are additional arguments, and (b) achieving an if-then-else structure in a macro that's fully resolved by the preprocessor.

The first problem can be solved using a technique I first saw in a reply to a question on stackoverflow:

#define CHILLIANT_LITERAL(...) __VA_ARGS__
#define CHILLIANT_VA_PICK__(a,b,c,d,e,f,g,h,i,j,...) j
#define CHILLIANT_VA_PICK_(a) \

    CHILLIANT_LITERAL(CHILLIANT_VA_PICK__)a
#define CHILLIANT_VA_PICK(a,b,...) \

    CHILLIANT_VA_PICK_((a##__VA_ARGS__##b,a##b))
#define CHILLIANT_VA_EMPTY__YTPME_AV_TNAILLIHC 0,0,0,0,0,0,0,0,0,1
#define CHILLIANT_VA_EMPTY(...) \

    CHILLIANT_VA_PICK(CHILLIANT_VA_EMPTY_, \
        _YTPME_AV_TNAILLIHC,__VA_ARGS__)

How this actually works is left as an exercise for the reader, but the bottom line is that "CHILLIANT_VA_EMPTY(...)" called with no arguments resolves to the token "1", whilst one or more arguments causes it to resolve to "0". The above code snippet only works for up to nine arguments, but it is trivial to expand to a greater number.

For completeness, here's the macro to return the count of the number of arguments:

#define CHILLIANT_VA_COUNT__TNUOC_AV_TNAILLIHC 9,8,7,6,5,4,3,2,1,0
#define CHILLIANT_VA_COUNT(...) \

    CHILLIANT_VA_PICK(CHILLIANT_VA_COUNT_, \
        _TNUOC_AV_TNAILLIHC,__VA_ARGS__)

Groovy, huh?

The second problem, generating the if-then-else semantic, is made difficult because we want it to resolve during the preprocessor phase, not during compilation. However, if we assume the condition is only ever the token "0" or "1" (which it is) the problem can be solved with careful identifier concatenation:

#define CHILLIANT_IGNORE(...)
#define CHILLIANT_IF_0(...) CHILLIANT_LITERAL
#define CHILLIANT_IF_1(...) __VA_ARGS__ CHILLIANT_IGNORE
#define CHILLIANT_IF(condition) \

    CHILLIANT_LITERAL(CHILLIANT_IF_)##CHILLIANT_LITERAL(condition)

This is used as follows:

CHILLIANT_IF(condition)(then_clause)(else_clause)

Now, we can construct the definition of "STATIC_ASSERT()":

#define STATIC_ASSERT(condition, ...) \
    static_assert(condition, \
        CHILLIANT_IF(CHILLIANT_VA_EMPTY(__VA_ARGS__)) \
            (#condition) \
            (__VA_ARGS__))

This does seem to be a lot of trouble to go to just to make the second argument of a static assert optional, but now you also have some of the basic building blocks to really go to town with the preprocessor.