c++visual-studio-2010c++11stringifystatic-assert

How to combine static_assert with sizeof and stringify?


Memory usage is quite critical in my application. Therefore I have specific asserts that check for the memory size at compile time and give a static_assert if the size is different from what we considered correct before.

I have defined a macro like this:

#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "!");

This macro makes it very easy to write this:

CHECKMEM(Book,144);
CHECKMEM(Library,80);

The problem is that when this static_assert goes off, it might be quite difficult to find out what the new size should be (e.g. by using the hidden compiler option "/d1 reportAllClassLayout"). It would be much handier if I could include the actual size, so instead of:

Size incorrect for Book!

It would show

Size incorrect for Book! (expected 144, size is 152)

I tried writing something like this:

#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "! (expected" #size ", size is " #sizeof(mytype) ")");

But you can't use the stringize (#) operator on a function call.

I also tried adding the double-stringize trick, like this:

#define STR1(x) #x 
#define STR2(x) STR1(x) 
#define CHECKMEM(mytype, size) static_assert((sizeof(objectType) == size)), "Size incorrect for " #mytype "! (expected" #size ", size is " STR2(sizeof(mytype)) ")");

But instead of printing size is 152 it prints size is sizeof(Book).

Is there a way to stringify the result of sizeof in a static_assert?


Solution

  • I'd use dispatching on a function template to do the checking:

    #include <cstddef>
    
    template <typename ToCheck, std::size_t ExpectedSize, std::size_t RealSize = sizeof(ToCheck)>
    void check_size() {
      static_assert(ExpectedSize == RealSize, "Size is off!");
    }
    
    struct foo
    {
      char bla[16];
    };
    
    int main()
    {
      check_size<foo, 8>();
      return 0;
    }
    

    Results in:

    In instantiation of ‘void check_size() [with ToCheck = foo; long unsigned int ExpectedSize = 8ul; long unsigned int RealSize = 16ul]’:
    bla.cpp:15:22:   required from here
    bla.cpp:5:1: error: static assertion failed: Size is off!
    

    The debugging information is in the template parameters of the back-trace.

    If this is truly better, you will have to decide and it also depends on the compiler. It also enables you to hide the expected size with a template map, to sum up to a max size and other fancy things.