在static_assert输出中整合types名称?
我喜欢提供有用的错误/消息,我也想为我的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 ]
一个标签在function模板本身中提到,另一个标签在类模板中提到。 不太好。 让我们看看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>."
看起来不错。 问题:我需要为每个模板创build一个这样的结构,因为static_assert
的错误信息需要是一个string文字…
现在,对于我的问题:我们可以以某种方式直接将types名称包含到static_assert
吗? 喜欢
static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">.");
示例输出:
无法从
Foo<int,main::TagB>
创buildFoo<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
断言并转储types,如果失败。
用法:
// 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
是检查的布尔结果
例
有关完整的SSCCE示例,请参阅: IDEOne示例
示例的错误消息:
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
,它将打印完整types的检查,例如: std::is_base_of<IMyInterface, MyBadType>
。 那么你确切地知道在失败的断言中使用了哪些types。
唯一的问题是这只适用于将结果放在::value
模板。 然而type_traits
主要使用这个,并且是goto标准。
如果你的编译器提供了__FUNCTION__
macros(到目前为止几乎无处不在),你可以使用它和字面连接进行一个非常简单的replace:
#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
有可能得到一个string文字作为模板的非typesparameter passing,并带有一点点的跳跃 。 但是由于static_assert
的第二个参数被限制为一个string字面值,而不是地址常量expression式,所以不幸的是没有多大用处。
可悲的是我怀疑你最好的办法是游说委员会或编译器作者来扩展这个设施。
std::type_info
有一个成员const char* name()
:
#include <typeinfo> using namespace std; //... const char* name = type_info(T).name();