在 static_assert 输出中集成类型名称?
我喜欢提供有用的错误/消息,我也想为我的 static_assert
这样做.问题是,它们依赖于模板参数.通常,由于引发的错误,这些参数会在途中或其他地方显示,但它们要么模糊不清,要么没有分组,因此它们是有意义的.示例:
I like to give helpful errors / messages, and I also want to do so for my static_assert
s. The problem is, that they depend on template parameters. Normally, those parameters will get displayed on way or an other due to the error raised, but they are either obscure or not grouped so they make sense. Example:
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 上的输出:
srcmain.cpp(74): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
srcmain.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 输出:
One tag is mentioned in the function template itself, the other below with the class template. Not so nice. Lets see what GCC outputs:
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
所在的位置.现在想象更多的参数,或更多的模板,或两者兼而有之.颤抖
Much better, but still not really where the static_assert
is. And now imagine some more parameters, or more templates, or both. shivers
解决这个问题的一种方法是使用中间结构,它将两个标签都作为模板参数:
One way to work around that is to use an intermediate struct, which takes both Tags as template parameters:
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;
}
};
现在让我们再次查看输出:
Now lets see the output again:
srcmain.cpp(70): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
srcmain.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
中的错误消息需要是字符串文字...
Looks not bad. The problem: I need to create such a struct for every template, since the error message in static_assert
needs to be a string literal...
现在,对于我的问题:我们能否以某种方式将类型名称直接包含在 static_assert
中?喜欢
Now, for my question: Can we somehow include the type names directly into the static_assert
? Like
static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">.");
示例输出:
无法从 Foo
创建 Foo
.
或者,如果这无法实现,我们能否以某种方式使错误消息成为额外的模板参数,以使其可通过?
Or, if that isn't achievable, can we somehow make the error message an extra template parameter, as to make it passable?
推荐答案
My Hack
代码:
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
断言并在失败时转储类型.
It allows for you to check any ::value
assertion and dump the types if it failed.
// 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
是检查的布尔结果
where std::my_check<...>::value
is the boolean result of the check
有关完整的 SSCCE 示例,请参阅:IDEOne 示例
For a full SSCCE example see: IDEOne Example
示例的错误信息:
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
.然后你就知道在失败的断言中使用了哪些类型.
Explanation
If the assertion fails, it will print the template arguments of AssertValue and therefore print the full template expansion of your check. For example, if you were checking a std::is_base_of
it will print the full type of the check, e.g.: std::is_base_of<IMyInterface, MyBadType>
. Then you know exactly what types were used in the failed assertion.
唯一的问题是这只适用于将结果放在 ::value
中的模板.然而,type_traits
主要使用这个并且是 goto 标准.
The only problem is that this only works on templates that put their result in ::value
. However type_traits
mostly uses this and is the goto standard.
相关文章