See what's going on with flipcode!




 

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.