17

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

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) 但我不想有警告,也不想删除代码来了解这些值。

4

6 回答 6

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

请注意,我将您的枚举更改为强类型,因为否则枚举的常量名称会发生​​冲突。如果你有弱类型枚举,传递给宏的 and 应该命名封闭范围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"

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

于 2012-12-12T10:54:12.483 回答
5

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

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

于 2017-07-16T09:32:29.993 回答
3

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

于 2012-12-12T12:36:13.930 回答
1

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");
     ^
于 2018-02-09T20:46:17.370 回答
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)
于 2016-03-22T07:45:53.457 回答
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>'

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));
^
于 2018-06-06T17:56:05.383 回答