Compile-Time Assertions Question submitted by n/a (10 October 2000) | Return to The Archives

| This entry is not a response to a question. |

| While working on the next Ask Midnight column entry (an n-dimensional matrix class that also acts as n-dimensional points and n-dimensional vectors) I discovered the need for compile-time assertions. So this article is presented as a preface to the next article.
I don't think anybody out there will disagree with the usefulness of assertions. If you disagree, please locate your elbow and promptly shove it up your nose. :)
Presented here is an assertion that looks just like a normal assert, but causes the compiler to produce an error. Hence, it is called the compile-time assert.
Compile-time errors are preferred over run-time errors because they notify you of a problem in your code before you even run the application (before you even finish building it, for that matter.) Unfortunately, you can't just replace all of your run-time assertions with compile-time assertions.
They do, however, have a very valid use. Consider the following example:

    template<int I>
    struct myClass
    {
      // ...

      int       foo();

      // ...
    };

But what if you only wanted 'foo' to be available to instantiations where the template parameter ('I') is an even number, or just a specific range of values?
I realize this is a rather esoteric example, but I wanted a simple example to be provided here. In the next Ask Midnight, you'll see a very valid use for these compile-time assertions.
Thus, the compile-time assertion was born...

    // Create a template structure called 'tAssert' that contains NO members

    template <bool B>
    struct tAssert
    {
    };

    // Specialization of the above template, with a single member: 'assert'

    struct tAssert<true>
    {
      static void assert() {};
    };

Note that the member function 'tAssert::assert()' only exists for templates created with a Boolean value of 'true' as the template parameter.
Here's how it works in practice:

    int foo()
    {
      tAssert<false>::assert();  // ERROR -- 'assert()' does not exist
      tAssert<true>::assert();   // OK -- 'assert()' exists
    }

An important difference between normal run-time asserts and compile-time asserts is that compile-time asserts can only be used with constants. Note in the following example, the use of the 'const' keyword:

    int foo()
    {
      const int a = 1;
      const int b = 2;
      tAssert< ((a*b) < 3) >::assert();
    }

Though, the ISO C++ specification allows this, many compilers do not (yet) support template instantiation from an expression. But we can fake it, like this:

    int foo()
    {
      const int  a = 1;
      const int  b = 2;
      const bool c = (bool) ( (a*b) < 3 ); // TRUE (c == 2 which is < 3)
      tAssert<c>::assert();                // OK.. compiles fine
    }

To clean it up, we'll use a macro.

    #define TAssert(a) {const bool b = (bool) (a); tAssert<b>::assert();}

    int foo()
    {
      const int  a = 1;
      const int  b = 2;
      TAssert(a*b<3);
    }

Unfortunately, we're still not done yet... because of two (ahem) "features" of the Visual C++ 6.0 compiler, our macro doesn't work. Changing the variable names helps, as does the addition of the trinary operator (?:) rather than a (bool) type-cast:

    #define TAssert(__a) {const bool __b = (__a) ? true : false; tAssert<__b>::assert();}

Now we have the compiler's full cooperation. Here is the entire thing reprinted for clarity:

    template <bool B> struct tAssert {};
    struct tAssert<true> { static void assert() {}; };
    #define TAssert(__a) {const bool __b = (__a) ? true : false; tAssert<__b>::assert();}

Errors are reported during compile-time, and each brand of compiler will have different error reporting and formatting, but here's what to expect from Visual C++ 6.0. Both of these errors point to the line of code containing 'TAssert()':

    foo.cpp(16) : error C2039: 'assert' : is not a member of 'tAssert<0>'
    foo.cpp(16) : error C2065: 'assert' : undeclared identifier

Response provided by Paul Nettle

This article was originally an entry in flipCode's Ask Midnight, a Question and Answer column with Paul Nettle that's no longer active.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.