Integrate type name in static_assert output?

I like to give useful errors / messages and I also want to do this for my static_assert s. The problem is that they depend on the template options. Typically, these parameters will be displayed one way or another due to an error, but they are either unclear 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); } 

Output to 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 ] 

One tag is mentioned in the function template itself, and the other below with the class template. Not very nice. Let's see what the GCC Outputs are :

 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>." 

Much better, but still not where the static_assert . Now imagine a few more options, or more templates, or both. goosebumps

One way around this is to use an intermediate structure that accepts 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 look again at the exit:

 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 ] 

Much better! Here GCC says :

 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>." 

Looks good. Problem: I need to create such a structure for each template, since the error message in static_assert should be a string literal ...

Now, in my question: can we somehow include type names directly in static_assert ? how

 static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">."); 

Output Example:

Cannot create Foo<int,main::TagA> from Foo<int,main::TagB> .

Or, if this is not possible, can we somehow make the error message an additional template parameter to make it available?

+45
c ++ c ++ 11 templates static-assert custom-errors
Jun 20 '11 at 17:40
source share
4 answers

My hack

The code:

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

It allows you to check any ::value statement and discard types if that fails.

Using:

 // Bad indentation used to show parts static_assert( AssertValue< std::my_check< T0, decltype(*somethingComplicated), T7::value_type > >, "something horrible happened" ); 

where std::my_check<...>::value is the logical result of the check

Example

For a complete SSCCE example , see: IDEOne Example

Example error message:

 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" 

Explanation

If the statement fails, it prints the arguments to the AssertValue template and, therefore, prints the full extension of your validation template. For example, if you checked std::is_base_of , it will print the full type of check, for example: std::is_base_of<IMyInterface, MyBadType> . Then you know exactly what types were used in the failed statement.

The only problem is that this only works with templates that put their result in ::value . However, type_traits mainly uses this and is the goto standard.

+12
Nov 13 '12 at 18:05
source share
β€” -

You can get a string literal passed as a template non-peak parameter, with a little hoop-jumping . But since the second argument to static_assert limited to a string literal rather than an address expression, unfortunately this is not very useful.

Unfortunately, I suspect that your best bet is to lobby the committee or authors of the compiler to expand the object.

+2
Oct 02 '11 at 18:21
source share

If your compiler provides the __FUNCTION__ macro, you can have a really simple replacement using it and literal concatenation. However, the implementation of gcc and clang is not executed as a macro, so this solution will not work for them.

 #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; } 

This creates the following result when compiling VS2015:

 static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD 
+2
Jul 27 '16 at 19:51
source share

std::type_info has a member const char* name() :

 #include <typeinfo> using namespace std; //... const char* name = type_info(T).name(); 
-four
Aug 18 2018-11-12T00:
source share



All Articles