在static_assert()中在编译时显示整数

时间:2012-12-12 10:26:41

标签: c++ macros c++11 integer 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: Parameterized error messages

Integrate type name in static_assert output?

最有趣的话题是这个: Printing sizeof(T) at compile time 但我不想发出警告或退出代码来了解这些值。

6 个答案:

答案 0 :(得分:4)

这基本上有效,尽管可以稍微省力(通过使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);

注意我将您的枚举更改为强类型,因为否则枚举的常量名称会发生​​冲突。如果您有弱类型枚举,则传递给宏的FirstSecond应该命名封闭范围。

现在,如果我注释掉其中一个值(因此枚举的大小不同),我会得到:

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"

查看整数值在不完整类型错误中的显示方式,以及静态断言中的符号名称?

答案 1 :(得分:2)

这是我找到的解决方案,我们收到一条带有值和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.

它基于此解决方案:Printing sizeof(T) at compile time

答案 2 :(得分:2)

首先在编译器输出中打印模板参数值的辅助类:

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&lt; 32,64&gt;'正在编制

答案 3 :(得分:0)

上述问题是它们依赖于可能存在或可能不存在于不同编译器上的警告,并且可能不会一直被所有人打开。 (事实上​​,其中一个没有显示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);

显然,它不适用于全范围的无符号长。作为额外的安全捕获,宏可以包含第二个直截了当的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)

答案 4 :(得分:0)

C++11decltype

#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

海湾合作委员会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");
     ^

答案 5 :(得分:0)

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>'

海湾合作委员会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));
^