在编译时以 static_assert() 格式显示整数

Display integer at compile time in static_assert()

本文关键字:格式 显示 整数 assert 编译 static      更新时间:2023-10-16

这是我正在尝试做的事情的简化版本

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?

最有趣的话题是这个:编译时的打印大小(T)但我不想有警告,或取消注释代码以了解值。

首先是一个帮助程序类,用于在编译器输出中打印模板参数值:

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>"的参考

这基本上有效,尽管可以通过一点努力来打破(通过将 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"

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

这是我

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

带有 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

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

上述问题在于它们依赖于警告,这些警告可能存在于不同的编译器上,也可能不存在,并且可能不是每个人都一直打开。(事实上,其中一个在打开所有警告的情况下不会显示 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)

另一种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));
^