Saturday, August 7, 2010

Static assert with a "real" message.

static asserts are important for many reasons and are heavily used in most large codebases esp when said codebase has many target environments. They inform the programmer when an assumption fails at compile time allowing for the runtime code to avoid expensive verification.
#define COMPILE_ASSERT(x) extern int __dummy[(int)x]

Which is typically used as
COMPILE_ASSERT(sizeof(int) == sizeof(unsigned));
COMPILE_ASSERT(sizeof(int) == sizeof(char));

the second example if obviously wrong and provides the output.
error C2466: cannot allocate an array of constant size 0

Which is not very useful other than as a signal that something bad occurred.

Now consider the form
#pragma warning (disable: 4483)

typedef char __identifier("compile assert:");

#define COMPILE_STRING_ASSERT(condition, name) \
typedef __identifier("compile assert:") __identifier(name)[1]; \
typedef __identifier("compile assert:") __identifier(name)[(condition) ? 1 : 0];

define COMPILE_ASSERT(condition) COMPILE_STRING_ASSERT(condition, #condition)

The above uses the identifier keyword which is technically an error however the pragma warning disables the error (yes it doesn't make sense). This keyword allows the user to define ANY identifier, valid C++ or not.
COMPILE_ASSERT(sizeof(int) == sizeof(char));

Now provides the output
'compile assert: sizeof(int) == sizeof(char)[1]'

which is a much more palatable message esp when considering automatic build systems.

  • __identifier is specific to Visual Studio and may not be supported by other compilers.
  • VC++ 2010 introduces static_assert for all targets, tho it is less powerful than using __identifier.
  • SNC may support both, i will try to confirm.


Reggie Meisler said...
This comment has been removed by the author.
Reggie Meisler said...

This is a pretty cool trick! Msvc 2010 has straight up static_assert and then some. It's definitely worth the switch over.

Also, the modern c++ design style static assert gives you a full error message (Granted it's a weird hack).

guardian said...

Andy, here is a link to a STATIC_ASSERT implementation of mine in case you're interested

sigmel said...

Similar to the one posted above, but in my opinion much cleaner:

template struct CompileTimeAssert;

template<> struct CompileTimeAssert{};

#define STATIC_ASSERT(e) (CompileTimeAssert <(e) != 0>())

guardian said...

sigmel > I got beaten by SFINAE several times: the static assertion was just ignored until I added the sizeof and typedef trickery

guardian said...

hmm also,

what do you mean when you say static_assert is less powerful than __identifier?