Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display integer at compile time in static_assert()

Here is a simplified version of what I'm trying to do

enum First
{
    a,
    b,
    c,
    nbElementFirstEnum,
};
enum Second
{
    a,
    b,
    c,
    nbElementSecondEnum,
};

static_assert(
    First::nbElementFirstEnum == Second::nbElementSecondEnum,
    "Not the same number of element in the enums.");
/*static_assert(  
    First::nbElementFirstEnum == Second::nbElementSecondEnum, 
    "Not the same number of element in the enums." + First::nbElementFirstEnum + " " + Second::nbElementSecondEnum);*/

But I would like to be able to print the value of First::nbElementFirstEnum and Second::nbElementSecondEnum in the assert message (like in the commented version which obviously doesn't work). I have tryed using macro concatenation with "#". I also tryed using variadic templates, retrieveing with %10 each number and adding the '0' character to the value retrieved, but all I get is a constexpr char[].

So my question is how can I get my enums values to be printed in a string literal.

Possible duplicates :

C++11 static_assert: Parameterized error messages

Integrate type name in static_assert output?

The most interesting topic was this one: Printing sizeof(T) at compile time But I don't want to have a warning, or decomment code to know the values.

like image 409
b3nj1 Avatar asked Dec 12 '12 10:12

b3nj1


People also ask

What is the difference between assert () and static_assert ()? Select one?

static_assert is meant to make compilation fail with the specified message, while traditional assert is meant to end the execution of your program.

What is static_assert C++?

Static assertions are a way to check if a condition is true when the code is compiled. If it isn't, the compiler is required to issue an error message and stop the compiling process. The condition that needs to be checked is a constant expression.

Where is static_assert defined?

static_assert is a keyword defined in the <assert. h> header. It is available in the C11 version of C. static_assert is used to ensure that a condition is true when the code is compiled.


2 Answers

This basically works, although it's possible to break with a little effort (by making V1 and V2 sum to a multiple of 256). So, I think your solution is uglier but still more robust.

template <int V1, int V2> struct AssertEquality
{
    static const char not_equal_warning = V1 + V2 + 256;
};

template <int V> struct AssertEquality<V, V>
{
    static const bool not_equal_warning = 0;
};

#define ASSERT_EQUALITY(V1, V2) static_assert( \
    AssertEquality<static_cast<int>(V1), \
                   static_cast<int>(V2)>::not_equal_warning == 0, \
    #V1 " != " #V2 );

// ...

ASSERT_EQUALITY(First::nbElementFirstEnum, Second::nbElementSecondEnum);

with output looking like:

g++ -std=c++0x -c chksz.cpp
chksz.cpp: In instantiation of ‘const char AssertEquality<3, 2>::not_equal_warning’:
chksz.cpp:40:124:   instantiated from here
chksz.cpp:5:53: warning: overflow in implicit constant conversion
chksz.cpp:40:1: error: static assertion failed: "First::nbElementFirstEnum != Second::nbElementSecondEnum"

For reference, this original version depended on gcc printing the static_assert message even when the boolean condition doesn't compile at all.

template <typename Enum1, int Max1, typename Enum2, int Max2>
struct AssertSameSizeEnums;

template <typename Enum1, int EnumMax, typename Enum2>
struct AssertSameSizeEnums<Enum1, EnumMax, Enum2, EnumMax> {};
// only define the special case where Max1 and Max2 have the same integer value

#define ASSERT_SAME_SIZE_ENUMS(E1, M1, E2, M2) static_assert( \
    sizeof(AssertSameSizeEnums<E1, E1::M1, E2, E2::M2>), \
    #E1 "::" #M1 " != " #E2 "::" #M2 );

enum class First {
    a, b, c, nbElementFirstEnum,
};
enum class Second {
    a, b, c, nbElementSecondEnum,
};

ASSERT_SAME_SIZE_ENUMS(First, nbElementFirstEnum, Second, nbElementSecondEnum);

Note I changed your enums to be strongly-typed, because otherwise the enumerated constant names clashed. If you have weakly-typed enums, the First and Second passed to the macro should name the enclosing scope.

Now, if I comment out one of the values (so the enums are different sizes), I get:

g++ -std=c++0x -c chksz.cpp
chksz.cpp:25:113: error: invalid application of ‘sizeof’ to incomplete type ‘AssertSameSizeEnums<First, 3, Second, 2>’
chksz.cpp:25:1: error: static assertion failed: "First::nbElementFirstEnum != Second::nbElementSecondEnum"

See how the integer values are displayed in the incomplete type error, and the symbolic names in the static assertion?

like image 189
Useless Avatar answered Oct 12 '22 07:10

Useless


First a helper class to print template argument values in compiler output:

template<size_t A, size_t B> struct TAssertEquality {
  static_assert(A==B, "Not equal");
  static constexpr bool _cResult = (A==B);
};

Then where you need to test it:

static constexpr bool _cIsEqual = 
  TAssertEquality<First::nbElementFirstEnum, Second::nbElementSecondEnum>::_cResult;

The compiler error message would look like:

note: see reference to class template instantiation 'TAssertEquality<32,64>' being compiled

like image 21
Serge Rogatch Avatar answered Oct 12 '22 05:10

Serge Rogatch