我喜欢提供有用的错误/消息,我也想为我的
static_assert
这样做。问题是,它们依赖于模板参数。通常,由于引发的错误,这些参数将显示在路上或其他地方,但它们要么是模糊的,要么是没有分组的,因此它们是有意义的。示例:
template<class T>
struct fake_dependency{
static bool const value = false;
};
template<class T, class Tag>
struct Foo{
Foo(){}
template<class OtherTag>
Foo(Foo<T, OtherTag> const&){
static_assert(fake_dependency<T>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
}
};
int main(){
Foo<int, struct TagA> fA;
Foo<int, struct TagB> fB(fA);
}
MSVC 上的输出:
src\main.cpp(74): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
src\main.cpp(84) : see reference to function template instantiation 'Foo<T,Tag>::Foo<main::TagA>(const Foo<T,main::TagA> &)' being compiled
with
[
T=int,
Tag=main::TagB
]
一个标签在函数模板本身中提到,另一个标签在下面的类模板中提到。不太好。让我们看看 GCC 输出什么:
prog.cpp: In constructor 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]':
prog.cpp:18:32: instantiated from here
prog.cpp:12:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."
好多了,但仍然不是真正的
static_assert
所在的位置。现在想象更多的参数,或更多的模板,或两者兼而有之。 瑟瑟发抖
解决这个问题的一种方法是使用中间结构,它将两个标签作为模板参数:
template<class Tag, class OtherTag>
struct static_Foo_assert{
static_assert(fake_dependency<Tag>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
};
template<class T, class Tag>
struct Foo{
Foo(){}
template<class OtherTag>
Foo(Foo<T, OtherTag> const&){
static_Foo_assert<Tag, OtherTag> x;
}
};
现在让我们再次查看输出:
src\main.cpp(70): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
src\main.cpp(79) : see reference to class template instantiation 'static_Foo_assert<Tag,OtherTag>' being compiled
with
[
Tag=main::TagB,
OtherTag=main::TagA
]
好多了!这是 GCC 所说的:
prog.cpp: In instantiation of 'static_Foo_assert<main()::TagB, main()::TagA>':
prog.cpp:17:40: instantiated from 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]'
prog.cpp:23:32: instantiated from here
prog.cpp:8:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."
看起来不错。问题:我需要为每个模板创建这样一个结构,因为
static_assert
中的错误消息需要是字符串文字...
现在,对于我的问题:我们能否以某种方式将类型名称直接包含到
static_assert
中?喜欢
static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">.");
输出示例:
无法从
创建Foo<int,main::TagA>
。Foo<int,main::TagB>
或者,如果这无法实现,我们能否以某种方式将错误消息作为额外的模板参数,以使其可以通过?
template <typename Assertion>
struct AssertValue : AssertionChecker<Assertion::value, Assertion>
{
static_assert(AssertionValue, "Assertion failed <see below for more information>");
static bool const value = Assertion::value;
};
它允许您检查任何
::value
断言并在失败时转储类型。
// Bad indentation used to show parts
static_assert(
AssertValue<
std::my_check<
T0, decltype(*somethingComplicated), T7::value_type
>
>,
"something horrible happened"
);
其中
std::my_check<...>::value
是检查的布尔结果
示例的错误消息:
prog.cpp: In instantiation of 'AssertValue<std::is_base_of<IMyInterface, MyBadType> >':
prog.cpp:37:69: instantiated from 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]'
prog.cpp:60:38: instantiated from here
prog.cpp:9:5: error: static assertion failed: "Assertion failed <see below for more information>"
prog.cpp: In function 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]':
prog.cpp:60:38: instantiated from here
prog.cpp:39:5: error: static assertion failed: "iterator passed does not reference IMyInterface items"
如果断言失败,它将打印 AssertValue 的模板参数,从而打印支票的完整模板扩展。例如,如果您正在检查
std::is_base_of
,它将打印支票的完整类型,例如:std::is_base_of<IMyInterface, MyBadType>
。然后您就可以确切地知道失败的断言中使用了哪些类型。
唯一的问题是,这仅适用于将结果放入
::value
的模板。然而 type_traits
主要使用这个并且是 goto 标准。
如果您的编译器提供了
__FUNCTION__
宏,您可以使用它和文字连接进行非常简单的替换。 然而,gcc 和 clang 的实现不是作为宏完成的,所以这个解决方案对他们不起作用。
#include "stdafx.h"
#include <type_traits>
template <class T>
class must_be_pod
{
static void test() { static_assert (std::is_pod<T>::value, __FUNCTION__ ": not a POD"); }
public:
must_be_pod() { test(); }
};
class not_a_pod
{
public:
not_a_pod() {}
virtual ~not_a_pod() {}
};
int main()
{
must_be_pod<not_a_pod> should_fail; // and it does
return 0;
}
用 VS2015 编译时会产生以下输出:
static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
可以获取作为模板非类型参数传入的字符串文字,并带有一点点hoop-jumping。但由于
static_assert
的第二个参数被限制为字符串文字,而不是地址常量表达式,不幸的是,这没有多大用处。
可悲的是,我怀疑你最好的选择是游说委员会或编译器编写者扩展该设施。
__FUNCSIG__
包括 MSVC 上的模板参数,以及 GCC 上的 __PRETTY_FUNCTION__
。
#if defined(_MSC_VER)
#define FUNSIG __FUNCSIG__
#elif defined(__GNUC__)
#define FUNSIG __PRETTY_FUNCTION__
#else
#define FUNSIG "Unknown"
#endif
...
static_assert(..., FUNSIG "failed");
请注意,
__func__
和__FUNCTION__
并不总是包含模板参数。
我看到这个问题不久前已经得到了回答,但完整的答案丢失了,我找到了一种非常简单的方法来达到预期的结果。
template <typename T, bool value>
static typename std::enable_if<value, void>::type FunctionWithReadableErrorMessage()
{
}
int main()
{
FunctionWithReadableErrorMessage<int, false>();
return 0;
}
如果 value=true,该函数将编译并且不起作用,否则我们会收到此错误消息:
main.cpp:在函数“int main()”中:main.cpp:16:50:错误:没有匹配 用于调用“FunctionWithReadableErrorMessage()”的函数 FunctionWithReadableErrorMessage(); ^
如果我们想要更通用一点,我们可以将其放入宏中