Question

I've been using static_assert (and the variants before standardization) quite heavily. One use that I'm sure many of us have them put to is ensuring that the size of sensitive data structures remain as assumed across platforms and configurations. For example:

class SizeSensitiveClass
{
    // ...
};

static_assert (sizeof(SizeSensitiveClass) == 18, "Check the size!");

Now, I've written a convenience macro to help with this particular use:

#define STATIC_ASSERT_SIZE(T, sz)  (sizeof(T) == (sz), "Size of '" #T "' doesn't match the expected value.")

Used like this:

STATIC_ASSERT_SIZE (SizeSensitiveClass, 18);

Which produces this output: (at compile time, obviously, in form of compile error)

Size of 'SizeSensitiveClass' doesn't match the expected value.

This is OK and nice, but I was wondering whether I can extend the implementation of this macro (keeping the interface intact) to output the current size and the expected size of the data structure as well. Ideally, the output should look something like:

Size of 'SizeSensitiveClass' doesn't match the expected value (20 vs. 18).

Even the current size would be extremely convenient. Is this possible?

I'm using VC12 (Visual C++ 2013) and GCC 4.8.1. I'd appreciate any solutions/techniques/methods that would be portable to at least these two.

I should mention that I have tried the common "stringize" trick, but it doesn't work (as one would have expected it not to.) It just produces the literal string sizeof(T) in the output.

I have a vague notion that this might be implemented using constexprs (to generate the message string) but I'm not familiar with them.

Was it helpful?

Solution

This might not be a solution in the sense you envision it, but it generates an error message which always contains the real size and the expected size close the error message from the static_assert:

#include <type_traits>

template< typename Type, std::size_t ExpectedSize, std::size_t ActualSize = 0 >
struct validate_size : std::true_type
{
    static_assert( ActualSize == ExpectedSize,
                   "actual size does not match expected size" );
};

template< typename Type, std::size_t ExpectedSize >
struct validate_size< Type, ExpectedSize, 0 >
  : validate_size< Type, ExpectedSize, sizeof( Type ) >
{};

int main()
{
    static_assert( validate_size< int, 4 >::value, "Oops" );
    static_assert( validate_size< int, 5 >::value, "Oops2" );
}

The error message from GCC 4.8:

main.cpp: In instantiation of 'struct validate_size<int, 5ul, 4ul>':
main.cpp:10:8:   required from 'struct validate_size<int, 5ul>'
main.cpp:15:43:   required from here
main.cpp:6:5: error: static assertion failed: actual size does not match expected size
     static_assert( ActualSize == ExpectedSize, "actual size does not match expected size" );
     ^

The message from Clang also contains the <int, 5ul, 4ul>-part, check for VS yourself.

Live example

Update: And you can obviously keep your interface intact:

#define STATIC_ASSERT_SIZE(T, sz) static_assert(validate_size<T,sz>::value, "")

OTHER TIPS

Stringize should work for the expected size. #sz expands to "18" (or whatever numeric literal you pass) when used in STATIC_ASSERT_SIZE.

If instead of a literal 18, you pass the name of another #define, then you would need the double-stringize trick in order to macro-expand sz before stringifying it.

Unfortunately the preprocessor doesn't know the value of sizeof(T), and static_assert has to take a string literal. So I think you're out of luck, although I may be missing something.

you can, please see c++ template programming. make a template class in which template para is an integer with value of size(T). make an compiler error into the class if the assertion is failed. the compiler will output the class name from which you can get back the value of size (T).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top