Отображать целое число во время компиляции в static_assert()

Вот упрощенная версия того, что я пытаюсь сделать

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);*/

Но я хотел бы иметь возможность печатать значение First::nbElementFirstEnum и Second::nbElementSecondEnum в сообщении об утверждении (как в закомментированной версии, которая явно не работает). Я попытался использовать конкатенацию макросов с помощью "#". Я также пытался использовать шаблоны с переменным числом аргументов, получая с помощью %10 каждое число и добавляя символ «0» к полученному значению, но все, что я получаю, — это constexpr char[].

Итак, мой вопрос заключается в том, как я могу получить значения перечислений для печати в строковом литерале.

Возможные дубликаты:

C++11 static_assert: параметризованные сообщения об ошибках

Добавить имя типа в вывод static_assert?

Самой интересной темой была следующая: Печать sizeof(T) во время компиляции Но я не хотите иметь предупреждение или раскомментировать код, чтобы узнать значения.


person b3nj1    schedule 12.12.2012    source источник


Ответы (6)


Это в основном работает, хотя его можно сломать с небольшим усилием (сделав сумму V1 и V2 кратной 256). Итак, я думаю, что ваше решение более уродливое, но все же более надежное.

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);

с выводом, похожим на:

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"

Для справки, эта исходная версия зависела от того, что gcc выводит сообщение static_assert, даже если логическое условие вообще не компилируется.

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);

Примечание. Я изменил ваши перечисления, чтобы они были строго типизированными, потому что в противном случае перечисляемые имена констант конфликтовали. Если у вас есть слабо типизированные перечисления, First и Second, переданные в макрос, должны называть объемлющую область.

Теперь, если я закомментирую одно из значений (поэтому перечисления имеют разные размеры), я получаю:

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"

Посмотрите, как целочисленные значения отображаются в ошибке неполного типа и символические имена в статическом утверждении?

person Useless    schedule 12.12.2012
comment
(Я использую gcc 4.7) Это странно, потому что вы не можете автоматически преобразовать строго типизированное перечисление в целое число, вам нужно использовать static_cast, тем не менее gcc останавливается на ошибке неполного типа и не отображает сообщение об ошибке static_assert. - person b3nj1; 12.12.2012
comment
Хорошо, я только что быстро протестировал более старую версию (4.5.1). Если вы измените макрос на использование static_cast<int>(E1::M1) и т. д., как это будет работать? - person Useless; 12.12.2012
comment
На самом деле нам не нужно отображать имя перечисления, только значения. Проблема в том, что gcc останавливается на ошибке неполного типа и не отображает сообщение об ошибке static_assert. Мы преуспели, вызвав предупреждение, чтобы static_assert был поднят. - person b3nj1; 12.12.2012
comment
Но ошибка неполного типа отображает значения перечисления, верно? - person Useless; 12.12.2012
comment
Да, они отображаются, но не сообщение об ошибке: / Я опубликую возможное решение. - person b3nj1; 12.12.2012
comment
У вас почему-то вообще не отображается значение под clang, даже с -Wconversion. Он отображает только ошибку static_assert failed. - person Jetski S-type; 22.03.2016
comment
Ах, изменение not_equal_warning со статической константы на статическую функцию constexpr вызывает предупреждение и отображает значения, но это вызывает другие проблемы с GCC... - person Jetski S-type; 22.03.2016
comment
попробуйте static const unsigned not_equal_warning = V1 ^ V2; - person TLW; 27.01.2019

Сначала вспомогательный класс для печати значений аргументов шаблона в выводе компилятора:

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

Тогда где вам нужно проверить это:

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

Сообщение об ошибке компилятора будет выглядеть так:

примечание: см. ссылку на компилируемый экземпляр шаблона класса 'TAssertEquality‹32,64>'

person Serge Rogatch    schedule 16.07.2017
comment
Измените его на ниже, чтобы избежать предупреждения о неиспользуемой переменной в '_cIsEqual' . (пусто) TAssertEquality‹First::nbElementFirstEnum, Second::nbElementSecondEnum›::_cResult; - person Deepak Yadav; 22.03.2019

Вот решение, которое я нашел, мы получаем предупреждающее сообщение со значениями и сообщением об ошибке static_assert.

template<int N>
struct TriggerOverflowWarning
{
    static constexpr char value() { return N + 256; }
};

template <int N, int M, typename Enable = void>
struct CheckEqualityWithWarning
{
    static constexpr bool value = true;
};

template <int N, int M>
struct CheckEqualityWithWarning<N, M, typename std::enable_if<N != M>::type>
{
    static constexpr bool value = (TriggerOverflowWarning<N>::value() == TriggerOverflowWarning<M>::value());
};

static constexpr int a = 9;
static constexpr int b = 10;

static_assert(CheckEqualityWithWarning<a, b>::value, "Mismatch.");

Вот вывод gcc:

g++ -std=c++11 -c test.cpp
test.cpp: In instantiation of 'static constexpr char TriggerOverflowWarning<N>::value() [with int N = 10]':
test.cpp:18:112:   required from 'constexpr const bool CheckEqualityWithWarning<9, 10>::value'
test.cpp:24:51:   required from here
test.cpp:6:52: warning: overflow in implicit constant conversion [-Woverflow]
test.cpp: In instantiation of 'static constexpr char TriggerOverflowWarning<N>::value() [with int N = 9]':
test.cpp:18:112:   required from 'constexpr const bool CheckEqualityWithWarning<9, 10>::value'
test.cpp:24:51:   required from here
test.cpp:6:52: warning: overflow in implicit constant conversion [-Woverflow]
test.cpp:24:5: error: static assertion failed: Mismatch.

Он основан на этом решении: Печать sizeof(T) во время компиляции

person b3nj1    schedule 12.12.2012
comment
Даже с выводом static_assert я не уверен, что он чище или читабельнее моего (честно говоря, он выглядит довольно шумно). Впрочем, это ваш вопрос и ваши требования. - person Useless; 12.12.2012
comment
Я должен согласиться с тем, что это шумно, но мне нужно, чтобы сообщение об ошибке И значения были напечатаны ... На данный момент это лучшее, что я могу сделать (я знаю, что это довольно уродливо, но я открыт для любого другого решения :). Ваше решение умное и хорошее, но сообщение об ошибке не печатается: / Какую версию gcc вы используете? (кстати, я предлагаю вам добавить static_casts в ваше решение). - person b3nj1; 12.12.2012
comment
Как я уже сказал, я использовал 4.5.1... печать сообщения, даже когда логическое условие не может быть скомпилировано, могла быть ошибкой. - person Useless; 12.12.2012
comment
Это решение требует флага -Wconversion для clang, чтобы оно работало. - person Jetski S-type; 22.03.2016

Проблема с вышеизложенным заключается в том, что они полагаются на предупреждения, которые могут существовать, а могут и не существовать в разных компиляторах, и не могут постоянно включаться всеми. (И на самом деле один из них не отображает значения для Clang при всех включенных предупреждениях.)

Это решение основано на других решениях здесь, но использует систему типов, поэтому все время является фактической ошибкой в ​​​​соответствии со стандартом C++. К сожалению, это останавливается раньше времени и не вызывает самой ошибки static_assert, что является недостатком. Это было протестировано на GCC 5.3 и Clang 3.7 без включения каких-либо предупреждений.

template <long V1, long V2>
struct StaticAssertEquality
{
  static constexpr void* NotEqualError() { return V1 + V2; }
};

template <long V>
struct StaticAssertEquality<V, V>
{
  static constexpr bool NotEqualError = true;
};

#define STATIC_ASSERT_LONG_EQUALITY(V1, V2)                          \
  static_assert(                                                     \
    StaticAssertEquality<static_cast<long>(V1),                      \
                         static_cast<long>(V2)>::NotEqualError,      \
    #V1 " != " #V2)

// ...

STATIC_ASSERT_LONG_EQUALITY(1, 2);

Очевидно, что он не работает для всего диапазона unsigned long. В качестве дополнительной меры предосторожности макрос может включать вторую простую static_assert(V1 == V2, #V1 " != " #V2); для обнаружения случайных совпадений при преобразовании типов.

Вывод для Clang выглядит следующим образом:

file.cpp: error: cannot initialize return object of type 'void *' with an rvalue of type 'long'
  static constexpr void* NotEqualError() { return V1 + V2; }
                                                  ^~~~~~~
file.cpp: note: in instantiation of member function 'StaticAssertEquality<1, 2>::NotEqualError' requested here
  STATIC_ASSERT_LONG_EQUALITY(1, 2);
  ^
file.cpp: note: expanded from macro 'STATIC_ASSERT_LONG_EQUALITY'
                              static_cast<long>(V2)>::NotEqualError,      \
                                                      ^
1 error generated.

И вывод с помощью GCC:

file.cpp: In instantiation of 'static constexpr void* StaticAssertEquality<V1, V2>::NotEqualError() [with long V1 = 1; long V2 = 2]':
file.cpp:   required from here
file.cpp: error: invalid conversion from 'long' to 'void*' [-fpermissive]
   static constexpr void* NotEqualError() { return V1 + V2; }
                                                        ^
g++.exe: failed with exit code 1 (00000001)
person Jetski S-type    schedule 22.03.2016
comment
На самом деле сообщение об ошибке static_assert сейчас почти не имеет значения, потому что вы его никогда не видите.... - person Jetski S-type; 22.03.2016

С C++11 и decltype:

#define UTILITY_PP_STRINGIZE_(x) #x
#define UTILITY_PP_STRINGIZE(x)  UTILITY_PP_STRINGIZE_(x)

#define STATIC_ASSERT_TRUE(exp, msg)    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp)>::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_TRUE1(exp, v1, msg) \
    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_TRUE2(exp, v1, v2, msg) \
    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_TRUE3(exp, v1, v2, v3, msg) \
    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)>, \
                  ::utility::StaticAssertParam<decltype(v3), (v3)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_TRUE4(exp, v1, v2, v3, v4, msg) \
    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)>, \
                  ::utility::StaticAssertParam<decltype(v3), (v3)>, \
                  ::utility::StaticAssertParam<decltype(v4), (v4)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)

#define STATIC_ASSERT_FALSE(exp, msg)   static_assert(::utility::StaticAssertFalse<decltype(exp), (exp)>::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_FALSE1(exp, v1, msg) \
    static_assert(::utility::StaticAssertFalse<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_FALSE2(exp, v1, v2, msg) \
    static_assert(::utility::StaticAssertFalse<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_FALSE3(exp, v1, v2, v3, msg) \
    static_assert(::utility::StaticAssertFalse<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)>, \
                  ::utility::StaticAssertParam<decltype(v3), (v3)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
#define STATIC_ASSERT_FALSE4(exp, v1, v2, v3, v4, msg) \
    static_assert(::utility::StaticAssertFalse<decltype(exp), (exp), \
                  ::utility::StaticAssertParam<decltype(v1), (v1)>, \
                  ::utility::StaticAssertParam<decltype(v2), (v2)>, \
                  ::utility::StaticAssertParam<decltype(v3), (v3)>, \
                  ::utility::StaticAssertParam<decltype(v4), (v4)> >::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)

#define STATIC_ASSERT_EQ(v1, v2, msg)   static_assert(::utility::StaticAssertEQ<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " == " UTILITY_PP_STRINGIZE(v2) "\": " msg)
#define STATIC_ASSERT_NE(v1, v2, msg)   static_assert(::utility::StaticAssertNE<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " != " UTILITY_PP_STRINGIZE(v2) "\": " msg)
#define STATIC_ASSERT_LE(v1, v2, msg)   static_assert(::utility::StaticAssertLE<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " <= " UTILITY_PP_STRINGIZE(v2) "\": " msg)
#define STATIC_ASSERT_LT(v1, v2, msg)   static_assert(::utility::StaticAssertLT<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " < "  UTILITY_PP_STRINGIZE(v2) "\": " msg)
#define STATIC_ASSERT_GE(v1, v2, msg)   static_assert(::utility::StaticAssertGE<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " >= " UTILITY_PP_STRINGIZE(v2) "\": " msg)
#define STATIC_ASSERT_GT(v1, v2, msg)   static_assert(::utility::StaticAssertGT<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " > "  UTILITY_PP_STRINGIZE(v2) "\": " msg)


namespace utility
{
    template <typename T, T v>
    struct StaticAssertParam
    {
    };

    template <typename T, T v, typename ...Params>
    struct StaticAssertTrue;

    template <typename T, T v>
    struct StaticAssertTrue<T, v>
    {
        static const bool value = (v ? true : false);
    };

    template <typename T, T v, typename ...Params>
    struct StaticAssertTrue
    {
        static const bool value = (v ? true : false);
        static_assert(v ? true : false, "StaticAssertTrue with parameters failed.");
    };

    template <typename T, T v, typename ...Params>
    struct StaticAssertFalse;

    template <typename T, T v>
    struct StaticAssertFalse<T, v>
    {
        static const bool value = (v ? false : true);
    };

    template <typename T, T v, typename ...Params>
    struct StaticAssertFalse
    {
        static const bool value = (v ? false : true);
        static_assert(v ? false : true, "StaticAssertFalse with parameters failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertEQ
    {
        static const bool value = (u == v);
        static_assert(u == v, "StaticAssertEQ failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertNE
    {
        static const bool value = (u != v);
        static_assert(u != v, "StaticAssertNE failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertLE
    {
        static const bool value = (u <= v);
        static_assert(u <= v, "StaticAssertLE failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertLT
    {
        static const bool value = (u < v);
        static_assert(u < v, "StaticAssertLT failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertGE
    {
        static const bool value = (u >= v);
        static_assert(u >= v, "StaticAssertGE failed.");
    };

    template <typename U, typename V, U u, V v>
    struct StaticAssertGT
    {
        static const bool value = (u > v);
        static_assert(u > v, "StaticAssertGT failed.");
    };
}

Использование:

struct A
{
    int a[4];
};

#define Float1 1.1f
#define Float2 1.2f

int main()
{
    static const int a = 3;
    static const long b = 5;
    static const long c = 7;
    static const long d = 9;
    STATIC_ASSERT_TRUE4(a == b && c == d, a, b, c, d, "long_expression_with_multiple_integral_variables");

    #pragma message("----------------------------------------")

    STATIC_ASSERT_TRUE(Float1 == Float2, "expression_with_floats");

    #pragma message("----------------------------------------")

    STATIC_ASSERT_EQ(10, sizeof(A), "simple_integral_expression_1");

    #pragma message("----------------------------------------")

    STATIC_ASSERT_EQ(11, sizeof(A), "simple_integral_expression_2");
    return 0;
}

MSVC2017:

source_file.cpp(72): error C2338: StaticAssertTrue with parameters failed.
source_file.cpp(148): note: see reference to class template instantiation 'utility::StaticAssertTrue<bool,false,utility::StaticAssertParam<const int,3>,utility::StaticAssertParam<const long,5>,utility::StaticAssertParam<const long,7>,utility::StaticAssertParam<const long,9>>' being compiled
source_file.cpp(148): error C2338: expression: "a == b && c == d": long_expression_with_multiple_integral_variables
----------------------------------------
source_file.cpp(152): error C2338: expression: "1.1f == 1.2f": expression_with_floats
----------------------------------------
source_file.cpp(95): error C2338: StaticAssertEQ failed.
source_file.cpp(156): note: see reference to class template instantiation 'utility::StaticAssertEQ<int,size_t,10,16>' being compiled
source_file.cpp(156): error C2338: expression: "10 == sizeof(A)": simple_integral_expression_1
----------------------------------------
source_file.cpp(160): error C2338: expression: "11 == sizeof(A)": simple_integral_expression_2

GCC 4.8.x:

<source>: In instantiation of 'struct utility::StaticAssertTrue<bool, false, utility::StaticAssertParam<const int, 3>, utility::StaticAssertParam<const long int, 5l>, utility::StaticAssertParam<const long int, 7l>, utility::StaticAssertParam<const long int, 9l> >':
<source>:148:5:   required from here
<source>:72:9: error: static assertion failed: StaticAssertTrue with parameters failed.
         static_assert(v ? true : false, "StaticAssertTrue with parameters failed.");
         ^
<source>: In function 'int main()':
<source>:18:5: error: static assertion failed: expression: "a == b && c == d": long_expression_with_multiple_integral_variables
     static_assert(::utility::StaticAssertTrue<decltype(exp), (exp), \
     ^
<source>:148:5: note: in expansion of macro 'STATIC_ASSERT_TRUE4'
     STATIC_ASSERT_TRUE4(a == b && c == d, a, b, c, d, "long_expression_with_multiple_integral_variables");
     ^
<source>:150:63: note: #pragma message: ----------------------------------------
     #pragma message("----------------------------------------")
                                                               ^
<source>:4:41: error: static assertion failed: expression: "1.1f == 1.2f": expression_with_floats
 #define STATIC_ASSERT_TRUE(exp, msg)    static_assert(::utility::StaticAssertTrue<decltype(exp), (exp)>::value, "expression: \"" UTILITY_PP_STRINGIZE(exp) "\": " msg)
                                         ^
<source>:152:5: note: in expansion of macro 'STATIC_ASSERT_TRUE'
     STATIC_ASSERT_TRUE(Float1 == Float2, "expression_with_floats");
     ^
<source>:154:63: note: #pragma message: ----------------------------------------
     #pragma message("----------------------------------------")
                                                               ^
<source>: In instantiation of 'struct utility::StaticAssertEQ<int, long unsigned int, 10, 16ul>':
<source>:156:5:   required from here
<source>:95:9: error: static assertion failed: StaticAssertEQ failed.
         static_assert(u == v, "StaticAssertEQ failed.");
         ^
<source>:44:41: error: static assertion failed: expression: "10 == sizeof(A)": simple_integral_expression_1
 #define STATIC_ASSERT_EQ(v1, v2, msg)   static_assert(::utility::StaticAssertEQ<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " == " UTILITY_PP_STRINGIZE(v2) "\": " msg)
                                         ^
<source>:156:5: note: in expansion of macro 'STATIC_ASSERT_EQ'
     STATIC_ASSERT_EQ(10, sizeof(A), "simple_integral_expression_1");
     ^
<source>:158:63: note: #pragma message: ----------------------------------------
     #pragma message("----------------------------------------")
                                                               ^
<source>: In instantiation of 'struct utility::StaticAssertEQ<int, long unsigned int, 11, 16ul>':
<source>:160:5:   required from here
<source>:95:9: error: static assertion failed: StaticAssertEQ failed.
         static_assert(u == v, "StaticAssertEQ failed.");
         ^
<source>:44:41: error: static assertion failed: expression: "11 == sizeof(A)": simple_integral_expression_2
 #define STATIC_ASSERT_EQ(v1, v2, msg)   static_assert(::utility::StaticAssertEQ<decltype(v1), decltype(v2), (v1), (v2)>::value, "expression: \"" UTILITY_PP_STRINGIZE(v1) " == " UTILITY_PP_STRINGIZE(v2) "\": " msg)
                                         ^
<source>:160:5: note: in expansion of macro 'STATIC_ASSERT_EQ'
     STATIC_ASSERT_EQ(11, sizeof(A), "simple_integral_expression_2");
     ^
person Andry    schedule 09.02.2018

Другой подход с C++11 просто для поиска глубоко вложенного типа или значения времени компиляции.

#include <vector>

// generates compilation error and shows real type name (and place of declaration in some cases) in an error message, useful for debugging boost::mpl like recurrent types
#define UTILITY_TYPE_LOOKUP_BY_ERROR(type_name) \
    using _type_lookup_t = decltype((*(typename ::utility::type_lookup<type_name >::type*)0).operator ,(*(::utility::dummy*)0))

// lookup compile time template typename value
#define UTILITY_PARAM_LOOKUP_BY_ERROR(static_param) \
    UTILITY_TYPE_LOOKUP_BY_ERROR(STATIC_ASSERT_PARAM(static_param))

#define STATIC_ASSERT_PARAM(v1) ::utility::StaticAssertParam<decltype(v1), (v1)>

namespace utility
{
    template <typename T>
    struct type_lookup
    {
        using type = T;
    };

    struct dummy {};

    template <typename T, T v>
    struct StaticAssertParam
    {
    };
}

struct test
{
    char a[123];
    double b[15];
    std::vector<int> c;
};

UTILITY_PARAM_LOOKUP_BY_ERROR(sizeof(test));

MSVC2017

error C2039: ',': is not a member of 'utility::StaticAssertParam<size_t,272>'

GCC 4.8.x:

<source>:5 : 103 : error : 'using type = struct utility::StaticAssertParam<long unsigned int, 272ul>' has no member named 'operator,'
using _type_lookup_t = decltype((*(typename ::utility::type_lookup<type_name >::type*)0).operator ,(*(::utility::dummy*)0))
^
<source> : 9 : 5 : note : in expansion of macro 'UTILITY_TYPE_LOOKUP_BY_ERROR'
UTILITY_TYPE_LOOKUP_BY_ERROR(STATIC_ASSERT_PARAM(static_param))
^
<source> : 36 : 1 : note : in expansion of macro 'UTILITY_PARAM_LOOKUP_BY_ERROR'
UTILITY_PARAM_LOOKUP_BY_ERROR(sizeof(test));
^
person Andry    schedule 06.06.2018