Вот упрощенная версия того, что я пытаюсь сделать
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 в сообщении assert (как в закомментированной версии, которая, очевидно, не работает).
Я попытался с помощью макросов с «#».
Я также попытался использовать шаблоны с переменными числами, получая с% 10 каждое число и добавляя символ «0» к полученному значению, но все, что я получаю, — это символ constexpr [].
Поэтому мой вопрос заключается в том, как я могу получить значения перечислений в виде строкового литерала.
Возможные дубликаты:
C ++ 11 static_assert: параметризованные сообщения об ошибках
Интегрировать имя типа в вывод static_assert?
Самая интересная тема была эта:
Печать sizeof (T) во время компиляции
Но я не хочу иметь предупреждение или код декомпозиции, чтобы знать значения.
Это в основном работает, хотя можно сломать с небольшим усилием (сделав сумму 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"
Посмотрите, как целочисленные значения отображаются в ошибке неполного типа, и символические имена в статическом утверждении?
Вот решение, которое я нашел, мы получаем предупреждающее сообщение со значениями и сообщение об ошибке 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) во время компиляции
Сначала вспомогательный класс для печати значений аргументов шаблона в выходных данных компилятора:
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> компилируется
Проблема с вышесказанным заключается в том, что они полагаются на предупреждения, которые могут существовать или не существовать в разных компиляторах и могут быть не включены всеми постоянно. (И действительно, один из них не отображает значения для 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
а также 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");
^
Другой подход с 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));
^