Guarding Against Code Bloat with static_assert

Powerful languages make it easy to overuse resources – especially memory. static_assert is a good way to detect these problems early.

In a recent project, I created a hash table with 256 buckets where I needed to add a random number generator to each bucket.

I have a nice general purpose random number generator that implements the mersenne twister. This requires a state buffer of about 2k, which is a fine size when you only need one or two of them.

So I implemented something like this:

class HashBucket
{
   Container<Objects> mObjects;
   MersenneTwister    mRand;
};

class HashWithRandom
{
    HashBucket mBuckets[256];
};

This of course blows up in spectacular fashion. 256 * 2k = 500k. So each instance of HashWithRandom will consume half a megabyte. This is bad – but manageable if you allocate dynamically. But if you create a couple of these on the stack you will quickly get a stack overflow.

On windows the symptoms are not particularly informative. In my case the program just exits immediately with no diagnositic message at all – not even a seg fault. Even in the MSVC debugger you don’t get a stack trace just the following error:

Unhandled exception at 0x00cbacb7 in test_session.exe: 0xC00000FD: Stack overflow.

The debugger’s stack trace is also uninformative:

test_session.exe!_chkstk()  Line 99	Asm
test_session.exe!__tmainCRTStartup()  Line 278 + 0x19 bytes	C
test_session.exe!mainCRTStartup()  Line 189	C

The problem is that the stack is being blown before any of your code runs at all. The startup routines crash as soon as they reserve space for the structures on the stack, even before the constructors run.

So I spent a few fun hours looking for stack corruption – buffer overruns and the like – which I didn’t find.


I Should have Used static_assert

I could have prevented this problem by creating a static_assert on the sizeof my object. static_assert is a feature of C++0x, which these days is all modern C++ compilers. It is an assertion that runs at compile time, and will generate a compile error if the condition fails.

So after declaring a class I can decide on a memory budget for the class, and write a static assert that will fire if we go over budget:

static_assert( sizeof( HashWithRandom ) < 4000, 
               "HashWithRandom is too large." );

You don’t have to put static asserts everywhere to get reasonable protection against classes the growing unexpectedly large.

Most systems will have all their component subsystems defined as members of a system class. Declaring a single static_assert for each of your major systems will protect against this kind of error should any subsystem’s static allocation grow too large:

class MySystem
{
   SubSystem1 mComponent1;
   SubSystem2 mComponent2;
   SubSystem3 mComponent3;
   SubSystem4 mComponent4;
   SubSystem5 mComponent5;
};

static_assert( sizeof( MySystem ) < 8000, 
               "MySystem is too large." );

Now, if any of the SubSystem classes grow too large you’ll get a compile time error, and get a chance to reconsider either your design, or the static allocation budget for the system as a whole:

src\mysystem.h(93) : error C2338: MySystem is too large.

Of course this only works if your classes contain their subsystems as members. If you follow a pattern of dynamically allocating sub-systems and only storing pointers to the subsystems in the parent class, then you’ll have to create static_asserts for each sub-system you want to be warned about.


Another Great Use for static_assert

Now that I’ve started using static_assert, there is at least one other place that it is really useful. If you have classes that depend on the size of the structures, you can assert on those.

So for example, say you have a serialization system that streams out primitive types. It will depend on the size of those types for proper serialization.

For example:

class Serial
{
public:
   Serial& operator<< ( int num )    { mStream.write( &num, 4 ); } 
   Serial& operator<< ( short num )  { mStream.write( &num, 2 ); } 
protected:
   Stream mStream;
};

It is a good idea to create a static_assert in this case, to let you know if any of your primitive types are an unexpected size:

static_assert( sizeof( int   ) != 4, "Serial: expected int to be 4 bytes" );
static_assert( sizeof( short ) != 2, "Serial: expected short to be 2 bytes" );

This is admittedly a toy example. I know I can use sizeof in the operator<<, but you still want to assert on the sizeof these types because if you serialize to systems with different sized primitives, then the serialization could still fail if you stream out on one system and stream in on another.

On modern systems you’re not likely to get problems with the size of primitive types ( but you still can on some embedded systems! ). More likely is if you try serializing structures where the size of the structure itself could vary depending on the structure packing from compiler to compiler.

You can also get problems like this with templated classes, or when you use types as a bitfield, and are expecting a known number of bits.

Basically anywhere you are depending on the size of a data structure that isn’t explicit in the code it is a good idea to static_assert on its size.

It also works as a documentation when someone else ( or even you months later ) need to understand the design.