23

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?

ildjarn
  • 60,915
  • 9
  • 122
  • 205
Patrick
  • 22,629
  • 9
  • 61
  • 127
  • 2
    Note that `sizeof` is *not* a function call – Konrad Rudolph Jul 17 '12 at 16:13
  • 2
    The trouble is that the second argument to `static_assert` must be a string literal and you cannot build such it in the preprocessor, as you can't use sizeof there. – pmr Jul 17 '12 at 16:27

5 Answers5

26

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.

pmr
  • 56,784
  • 10
  • 109
  • 154
5

Depending on your compiler, templates may be able to help:

template<int s, int t> struct check_size {
  static_assert(s == t, "wrong size");
};
check_size<2+2, 5> doubleplusungood;

gcc outputs:

prog.cpp: In instantiation of 'check_size<4, 5>':
prog.cpp:5:20:   instantiated from here
prog.cpp:2:3: error: static assertion failed: "wrong size"
ecatmur
  • 145,219
  • 25
  • 281
  • 356
2

As you discovered, the problem is here (also see this very similar question) :

#define CHECKMEM(mytype, size)  #sizeof(mytype)

It is not possible to do, because the stringification is done by the preprocessor, and sizeof is evaluated during the compilation.

Community
  • 1
  • 1
BЈовић
  • 59,719
  • 40
  • 167
  • 261
0

Here's an alternative header-only solution if you can modify the struct's definition a bit and don't mind some ugliness.

template <int RealSize = 0, int ExpectedSize = 0>
struct _MyStruct {
    static_assert(RealSize == ExpectedSize, "size is invalid");

    int x;
};

typedef _MyStruct<sizeof(_MyStruct<>), 4> MyStruct;

clang outputs:

prog.cpp:4:5: error: static_assert failed "size is invalid"
    static_assert(RealSize == ExpectedSize, "size is invalid");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~
prog.cpp:12:14: note: in instantiation of template class '_MyStruct<4, 8>' requested here
    MyStruct m;

One caveat here is that the check will only occur if you instantiate the type somewhere -- just using a pointer won't trigger the error, so definitely not a great fit for all situations!

Dustin Spicuzza
  • 439
  • 5
  • 10
0

A simple practical solution is to use 2 static_assert-s:

#define CHECKMEM(mytype, size) \
static_assert( sizeof(mytype) <= (size), "Size of " #mytype " is greater than " #size "!" ); \
static_assert( sizeof(mytype) >= (size), "Size of " #mytype " is less than "    #size "!" )
arcadius
  • 321
  • 2
  • 4