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, tho
If your compiler provides the __FUNCTION__
macro, you can have a really simple substitution using it and literal concatenation. However, gcc's and clang's implementation is not done 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 produces the following output when compiled by VS2015:
static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
It's possible to get a string literal passed in as a template non-type parameter, with a little bit of hoop-jumping. But since the second argument to static_assert
is constrained to be a string literal rather than, say, an address constant expression, this unfortunately is not much use.
Sadly I suspect your best bet is to lobby the committee or the compiler writers to extend the facility.
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 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"
);
where std::my_check<...>::value
is the boolean result of the check
For a full SSCCE example see: IDEOne Example
The Example's 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"
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.
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.
std::type_info
has a member const char* name()
:
#include <typeinfo>
using namespace std;
//...
const char* name = type_info(T).name();
I see this has been answered a while ago, but the full answer was lost, and I found a very simply way to achieve the desired result.
template <typename T, bool value>
static typename std::enable_if<value, void>::type FunctionWithReadableErrorMessage()
{
}
int main()
{
FunctionWithReadableErrorMessage<int, false>();
return 0;
}
This function will compile and have no effect if value=true, otherwise we get this error message:
main.cpp: In function 'int main()': main.cpp:16:50: error: no matching function for call to 'FunctionWithReadableErrorMessage()' FunctionWithReadableErrorMessage(); ^
If we wanna be a bit more general we can put it in a macro