Related
I am working with a library which exposes an interface to work with. One of the functions of this library is like this :
template <int a>
void modify(){}
I have to modify parameters from 1 to 10 i.e. call modify with with template arguments from 1 to 10. For that I wrote this code (a basic version of code, actual code is much larger).
for(int i=0; i<10; i++){
modify<i>();
}
On compilation I receive the following error
error: 'i' cannot appear in constant-expression
After going through some links on the internet, I came to know that I cannot pass any value as template argument which is not evaluated at compile time.
My question are as follows:
1. Why can't compiler evaluate i at compile time?
2. Is there any other to achieve the objective I am trying to achieve without changing the API interface?
There is another thing I want to do. Call modify as modify where VAR is the output of some functional computation. How can I do that?
What is the value of i (that is not a constant) at compile time? There is no way to answer unless executing the loop. But executing is not "compiling"
Since there is no answer, the compiler cannot do that.
Templates are not algorithm to be executed, but macros to be expanded to produce code.
What you can do is rely on specialization to implement iteration by recursion, like here:
#include <iostream>
template<int i>
void modify()
{ std::cout << "modify<"<<i<<">"<< std::endl; }
template<int x, int to>
struct static_for
{
void operator()()
{ modify<x>(); static_for<x+1,to>()(); }
};
template<int to>
struct static_for<to,to>
{
void operator()()
{}
};
int main()
{
static_for<0,10>()();
}
Note that, by doing this, you are, in fact, instantiating 10 functions named
modify<0> ... modify<9>, called respectively by static_for<0,10>::operator() ... static_for<9,10>::operator().
The iteration ends because static_for<10,10> will be instantiated from the specialization that takes two identical values, that does nothing.
"Why can't compiler evaluate i at compile time?"
That would defeat the purpose of templates. Templates are there for the case where the source code looks the same for some set of cases, but the instructions the compiler needs to generate are different each time.
"Is there any other to achieve the objective I am trying to achieve without changing the API interface?"
Yes, look at Boost.MPL.
However I suspect the right answer here is that you want to change the API. It depends on the internals of the modify function. I know you have it's source, because templates must be defined in headers. So have a look why it needs to know i at compile time and if it does not, it would be best to replace (or complement if you need to maintain backward compatibility) it with normal function with parameter.
Since you asked for an answer using Boost.MPL:
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>
template <int N>
void modify()
{
std::cout << N << '\n';
}
// You need to wrap your function template in a non-template functor
struct modify_t
{
template <typename N>
void operator()(N)
{
modify<N::value>();
}
};
int main()
{
namespace mpl = boost::mpl;
mpl::for_each< mpl::range_c<int,0,10> >( modify_t() ); // prints 0 to 9
}
Without using struct or Boost it can also be done :
#include <iostream>
#include <utility>
template <int a>
void modify()
{
std::cout<<a<<",";
}
template<int i,size_t... t>
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>)
{
bool kai[]= { (modify<i+t>(), false)...};
}
template<int i,int n>
constexpr inline void CT_for()
{
CT_for_impl<i>(std::make_index_sequence<n-i+1>());
}
int main()
{
CT_for<-5,5>();
return 0;
}
Given you want to call the functions at run-time by their index and you can't change the API, you can consider type-erasure:
std::vector<std::function<void(int)> > func;
func.push_back(modify<1>);
func.push_back(modify<2>);
//... and so on ...
func.push_back(modify<10>);
for(int i=0; i<10; ++i)
{
func[i](); //calls modify<i+1>();
}
Some points to mention:
That's not what templates are primarily for, but it's a way to bring a static library to the run-time world. The basic requirement for this is that one works with homogeneous types (--if modify<7>() would return, say, a std::string the whole approach would break).
The previous solution using type-erasure has an overhead. One can maybe get it faster by using function pointers, but still it will always be slower than calling the functions at compile time.
One can (and should) also wrap the push_backs into another iterative static function to avoid the manual calls.
solution to error: 'i' cannot appear in constant-expression for the above problem
To read about constexpr click this link
#include <iostream>
using namespace std;
template <typename T>
void modify(T a)
{
cout<<a<<endl; //to check if its working
}
//func converts int a into const int a
constexpr int func(int a)
{
return a;
}
int main(){
for(int i=0; i<10; i++){
modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr
}
return 0;
}
I am working with a library which exposes an interface to work with. One of the functions of this library is like this :
template <int a>
void modify(){}
I have to modify parameters from 1 to 10 i.e. call modify with with template arguments from 1 to 10. For that I wrote this code (a basic version of code, actual code is much larger).
for(int i=0; i<10; i++){
modify<i>();
}
On compilation I receive the following error
error: 'i' cannot appear in constant-expression
After going through some links on the internet, I came to know that I cannot pass any value as template argument which is not evaluated at compile time.
My question are as follows:
1. Why can't compiler evaluate i at compile time?
2. Is there any other to achieve the objective I am trying to achieve without changing the API interface?
There is another thing I want to do. Call modify as modify where VAR is the output of some functional computation. How can I do that?
What is the value of i (that is not a constant) at compile time? There is no way to answer unless executing the loop. But executing is not "compiling"
Since there is no answer, the compiler cannot do that.
Templates are not algorithm to be executed, but macros to be expanded to produce code.
What you can do is rely on specialization to implement iteration by recursion, like here:
#include <iostream>
template<int i>
void modify()
{ std::cout << "modify<"<<i<<">"<< std::endl; }
template<int x, int to>
struct static_for
{
void operator()()
{ modify<x>(); static_for<x+1,to>()(); }
};
template<int to>
struct static_for<to,to>
{
void operator()()
{}
};
int main()
{
static_for<0,10>()();
}
Note that, by doing this, you are, in fact, instantiating 10 functions named
modify<0> ... modify<9>, called respectively by static_for<0,10>::operator() ... static_for<9,10>::operator().
The iteration ends because static_for<10,10> will be instantiated from the specialization that takes two identical values, that does nothing.
"Why can't compiler evaluate i at compile time?"
That would defeat the purpose of templates. Templates are there for the case where the source code looks the same for some set of cases, but the instructions the compiler needs to generate are different each time.
"Is there any other to achieve the objective I am trying to achieve without changing the API interface?"
Yes, look at Boost.MPL.
However I suspect the right answer here is that you want to change the API. It depends on the internals of the modify function. I know you have it's source, because templates must be defined in headers. So have a look why it needs to know i at compile time and if it does not, it would be best to replace (or complement if you need to maintain backward compatibility) it with normal function with parameter.
Since you asked for an answer using Boost.MPL:
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>
template <int N>
void modify()
{
std::cout << N << '\n';
}
// You need to wrap your function template in a non-template functor
struct modify_t
{
template <typename N>
void operator()(N)
{
modify<N::value>();
}
};
int main()
{
namespace mpl = boost::mpl;
mpl::for_each< mpl::range_c<int,0,10> >( modify_t() ); // prints 0 to 9
}
Without using struct or Boost it can also be done :
#include <iostream>
#include <utility>
template <int a>
void modify()
{
std::cout<<a<<",";
}
template<int i,size_t... t>
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>)
{
bool kai[]= { (modify<i+t>(), false)...};
}
template<int i,int n>
constexpr inline void CT_for()
{
CT_for_impl<i>(std::make_index_sequence<n-i+1>());
}
int main()
{
CT_for<-5,5>();
return 0;
}
Given you want to call the functions at run-time by their index and you can't change the API, you can consider type-erasure:
std::vector<std::function<void(int)> > func;
func.push_back(modify<1>);
func.push_back(modify<2>);
//... and so on ...
func.push_back(modify<10>);
for(int i=0; i<10; ++i)
{
func[i](); //calls modify<i+1>();
}
Some points to mention:
That's not what templates are primarily for, but it's a way to bring a static library to the run-time world. The basic requirement for this is that one works with homogeneous types (--if modify<7>() would return, say, a std::string the whole approach would break).
The previous solution using type-erasure has an overhead. One can maybe get it faster by using function pointers, but still it will always be slower than calling the functions at compile time.
One can (and should) also wrap the push_backs into another iterative static function to avoid the manual calls.
solution to error: 'i' cannot appear in constant-expression for the above problem
To read about constexpr click this link
#include <iostream>
using namespace std;
template <typename T>
void modify(T a)
{
cout<<a<<endl; //to check if its working
}
//func converts int a into const int a
constexpr int func(int a)
{
return a;
}
int main(){
for(int i=0; i<10; i++){
modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr
}
return 0;
}
I am working with a library which exposes an interface to work with. One of the functions of this library is like this :
template <int a>
void modify(){}
I have to modify parameters from 1 to 10 i.e. call modify with with template arguments from 1 to 10. For that I wrote this code (a basic version of code, actual code is much larger).
for(int i=0; i<10; i++){
modify<i>();
}
On compilation I receive the following error
error: 'i' cannot appear in constant-expression
After going through some links on the internet, I came to know that I cannot pass any value as template argument which is not evaluated at compile time.
My question are as follows:
1. Why can't compiler evaluate i at compile time?
2. Is there any other to achieve the objective I am trying to achieve without changing the API interface?
There is another thing I want to do. Call modify as modify where VAR is the output of some functional computation. How can I do that?
What is the value of i (that is not a constant) at compile time? There is no way to answer unless executing the loop. But executing is not "compiling"
Since there is no answer, the compiler cannot do that.
Templates are not algorithm to be executed, but macros to be expanded to produce code.
What you can do is rely on specialization to implement iteration by recursion, like here:
#include <iostream>
template<int i>
void modify()
{ std::cout << "modify<"<<i<<">"<< std::endl; }
template<int x, int to>
struct static_for
{
void operator()()
{ modify<x>(); static_for<x+1,to>()(); }
};
template<int to>
struct static_for<to,to>
{
void operator()()
{}
};
int main()
{
static_for<0,10>()();
}
Note that, by doing this, you are, in fact, instantiating 10 functions named
modify<0> ... modify<9>, called respectively by static_for<0,10>::operator() ... static_for<9,10>::operator().
The iteration ends because static_for<10,10> will be instantiated from the specialization that takes two identical values, that does nothing.
"Why can't compiler evaluate i at compile time?"
That would defeat the purpose of templates. Templates are there for the case where the source code looks the same for some set of cases, but the instructions the compiler needs to generate are different each time.
"Is there any other to achieve the objective I am trying to achieve without changing the API interface?"
Yes, look at Boost.MPL.
However I suspect the right answer here is that you want to change the API. It depends on the internals of the modify function. I know you have it's source, because templates must be defined in headers. So have a look why it needs to know i at compile time and if it does not, it would be best to replace (or complement if you need to maintain backward compatibility) it with normal function with parameter.
Since you asked for an answer using Boost.MPL:
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>
template <int N>
void modify()
{
std::cout << N << '\n';
}
// You need to wrap your function template in a non-template functor
struct modify_t
{
template <typename N>
void operator()(N)
{
modify<N::value>();
}
};
int main()
{
namespace mpl = boost::mpl;
mpl::for_each< mpl::range_c<int,0,10> >( modify_t() ); // prints 0 to 9
}
Without using struct or Boost it can also be done :
#include <iostream>
#include <utility>
template <int a>
void modify()
{
std::cout<<a<<",";
}
template<int i,size_t... t>
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>)
{
bool kai[]= { (modify<i+t>(), false)...};
}
template<int i,int n>
constexpr inline void CT_for()
{
CT_for_impl<i>(std::make_index_sequence<n-i+1>());
}
int main()
{
CT_for<-5,5>();
return 0;
}
Given you want to call the functions at run-time by their index and you can't change the API, you can consider type-erasure:
std::vector<std::function<void(int)> > func;
func.push_back(modify<1>);
func.push_back(modify<2>);
//... and so on ...
func.push_back(modify<10>);
for(int i=0; i<10; ++i)
{
func[i](); //calls modify<i+1>();
}
Some points to mention:
That's not what templates are primarily for, but it's a way to bring a static library to the run-time world. The basic requirement for this is that one works with homogeneous types (--if modify<7>() would return, say, a std::string the whole approach would break).
The previous solution using type-erasure has an overhead. One can maybe get it faster by using function pointers, but still it will always be slower than calling the functions at compile time.
One can (and should) also wrap the push_backs into another iterative static function to avoid the manual calls.
solution to error: 'i' cannot appear in constant-expression for the above problem
To read about constexpr click this link
#include <iostream>
using namespace std;
template <typename T>
void modify(T a)
{
cout<<a<<endl; //to check if its working
}
//func converts int a into const int a
constexpr int func(int a)
{
return a;
}
int main(){
for(int i=0; i<10; i++){
modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr
}
return 0;
}
This question already has answers here:
Is there a way to pass auto as an argument in C++?
(4 answers)
auto parameter type in functions
(1 answer)
Closed 4 months ago.
Is it possible to create a generic C++ function foo?
foo(Object bar, Object fred)
{
//code
}
in which that if the two objects are recognized, they are compared and a comparison value is returned otherwise some other value is returned to indicate a comparison was not possible?
I ask in the case of genericizing a sorting class, in which case you can use this method, and when you derive new objects you want to sort, you add to this foo function, a method on which to sort the new type of Object.
Using templates, define two versions of the function, one where the parameters are the same type and one where they can be different:
#include <string>
#include <iostream>
using namespace std;
template<typename Type>
void func(Type, Type)
{
cout << "same" << endl;
}
template<typename TypeA, typename TypeO>
void func(TypeA, TypeO)
{
cout << "different" << endl;
}
int main()
{
func(5, 3); // same
func(5, 3.0); // different
func(string("hello"), "hello"); // different
func(5.0, 3.0); // same
return 0;
}
Output:
same
different
different
same
I think you are in dire need of Templates!
You can write a template function and then write a specialization for the said types to do something specific if the need be.
template<class Type1, class Type2>
void foo(Type1 t1, Type2 t2)
{
// put code here for function
}
call as
foo<std::string, int> ("hello", 10);
Most probably you need to use templates as other people suggest:
template <class T>
return_type func(T const& l, T const& r)
{
...
}
Because you normally want compilation to fail when the operation implemented by a generic function does not make sense for particular types, so you would either use conditional definition (in the below example is_arithmetic):
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
template <class T>
typename boost::enable_if<boost::is_arithmetic<T>, return_type>::type
func(T const& l, T const& r)
{
...
}
or static assertion in the code to yield the same result:
#include <boost/type_traits/is_arithmetic.hpp>
template <class T>
return_type func(T const& l, T const& r)
{
static_assert(boost::is_arithmetic<T>::type::value, "incompatible types");
...
}
I'm going to stick my neck out here and say you don't need Templates to do this. I'm not saying don't use them, but just that depending on exactly what you're wanting to do, there are alternatives.
What it sounds like you want is the ability to compare two generic objects provided that they adhere to a common set of ground rules. You could actually implement this using traditional inheritance or using templates. The choice of which you want comes down to how flexible you need it to be and whether you want some of the decisions to be made at runtime or compile time. If the latter - i.e. you want to pick up on casting errors etc., - then go for templates.
Either way, your objects will either have to adhere to some basic groundrules for how you compare them and preferably encapsulate that - this way your comparitor would be generic. or you'd have to write different comparitors for each object comparison. While it sounds like the latter is what you want, be wary of letting too much of your class implementation leach out into the comparitor function and thereby breaking encapsulation.
From my own experience, going straight to the template approach can occasionally result in a lot of bloated, messed up code which is hard to read, debug and maintain. Take a hard look at you design and what you actually need first.
OP seems to want to know if the 2 objects are comparable or not. You can use template specialization to achieve this (note: this doesn't compile on VC 10, but does on g++ 4.7). The only nuance, is you want this function to
they are compared and a comparison value is returned otherwise some other value is returned to indicate a comparison was not possible
But you need to define some sort of structure to signify that a comparison was not possible; using a magic number '-500' or whatever is not good style. Alternately, you could throw an error, and allow it to be caught an handled.
struct NoCompare{};
template <typename U1, typename U2>
static auto compare2(const U1 & u1, const U2 & u2) -> decltype(u1 == u2)
{
cout << "Comparable" << endl;
return u1 == u2;
}
static int compare2(...)
{
// Comparison not supported - return whatever value you want. (change the return type as appropriate)
cout << "Not comparable" << endl;
return -500;
}
int main()
{
int a = 5, b = 3, c = 3;
NoCompare dns;
cout << compare2(a, b) << endl;
cout << compare2(dns, b) << endl;
cout << compare2(c, b) << endl;
return 0;
}
Output:
C:\MinGW\MinGW>a
Comparable
0
Not comparable
-500
Comparable
1
It seems that you are referring to Common Lisp / CLOS -style generic functions which do multiple dynamic dispatch. C++ does single dynamic dispatch with methods but only single static dispatch with functions. So the answer is no. C++ doesn't support this at the moment. There have been proposals along the years to add it into the language but that hasn't happened yet.
Is it possible in C++ to stringify template arguments?
I tried this:
#include <iostream>
#define STRINGIFY(x) #x
template <typename T>
struct Stringify
{
Stringify()
{
std::cout << STRINGIFY(T) << endl;
}
};
int main()
{
Stringify<int> s;
}
But what I get is a T, and not an int. Seems that the preprocessor macros are evaluated before template instantiation.
Is there any other way to do this?
Is there any way for the preprocessing to take place after template instantiation? (Compiler is VC++).
You could try
typeid(T).name()
Edit: Fixed based on comments.
You could use some template magic.
#include <iostream>
template <typename T>
struct TypeName { static const char *name; };
template <typename T>
const char *TypeName<T>::name = "unknown";
template <>
const char *TypeName<int>::name = "int";
template <typename T>
struct Stringify
{
Stringify()
{
std::cout << TypeName<T>::name << std::endl;
}
};
int main()
{
Stringify<int> s;
}
This has an advantage over RTTI (i.e. typeinfo) - it is resolved during compilation; and disadvantage - you need to provide type information yourself (unless there is some library that does that already that I'm not aware of; maybe something in Boost even).
Or, as Martin York suggested in comments, use inline function templates instead:
template <typename T>
inline const char* typeName(void) { return "unknown"; }
template <>
inline const char* typeName<int>(void) { return "int"; }
// ...
std::cout << typeName<T>() << std::endl;
But, if you'll ever need to store more information about that particular type, then class templates will probably be better.
Your code doesn't work because the preprocessor, responsible for searching and expanding the macros you use in your code, is not aware of the language itself. It is just a text parser. It finds that STRINGIFY(T) in the very function template and expand it, much before you give a type to that template. As it turns out, you will always get "T" instead of the typename you expected, unfortunately.
As litb suggested, I've (badly) implemented this `getTypeName' function template that returns the typename you pass it:
#include <iostream>
template <typename _Get_TypeName>
const std::string &getTypeName()
{
static std::string name;
if (name.empty())
{
const char *beginStr = "_Get_TypeName =";
const size_t beginStrLen = 15; // Yes, I know...
// But isn't it better than strlen()?
size_t begin,length;
name = __PRETTY_FUNCTION__;
begin = name.find(beginStr) + beginStrLen + 1;
length = name.find("]",begin) - begin;
name = name.substr(begin,length);
}
return name;
}
int main()
{
typedef void (*T)(int,int);
// Using getTypeName()
std::cout << getTypeName<float>() << '\n';
std::cout << getTypeName<T>() << '\n'; // You don't actually need the
// typedef in this case, but
// for it to work with the
// typeid below, you'll need it
// Using typeid().name()
std::cout << typeid(float).name() << '\n';
std::cout << typeid(T).name() << '\n';
return 0;
}
The code above results in the following output with GCC flag -s ("strip all symbols from binary") enabled:
float
void (*)(int, int)
f
PFviiE
So, you see, getTypename() does a fairly better job, at the cost of that fugly string parsing hack (I KNOW, it's damn ugly).
A few points to take into account:
The code is GCC only. I don't know how to port it to another compiler. Probably only a few others have such a facility to produce so pretty function names, and from what I searched, MSVC++ doesn't have one, if you're asking yourself that.
If, in a new version, GCC formats __PRETTY_FUNCTION__'s differently, the string matching can break and you'll have to fix it. For this same reason I also warn that getTypeName() might be good for debugging (and, still, maybe not even good for that), but it is surely bad, bad, and bad for other purposes such as comparing two types in a template or something like that (I don't know, just guessing what someone might think of..). Use it solely for debugging, and preferentially don't call it in release builds (use macros to disable), so that you don't use __PRETTY_FUNCTION__ and thus the compiler doesn't produce the string for it.
I'm definitely no expert, and I'm not sure whether some odd type could cause the string matching to fail. I'd like to ask for people who read this post to comment if they know of such a case.
The code uses a static std::string. It means that, if some exception is thrown from its constructor or destructor, there is no way that it will reach a catch block and you'll get an unhandled exception. I don't know whether std::strings can do that, but beware that, if they do, you're potentially in trouble. I used it because it needs a destructor to free the memory. You could implement your own class for that, though, ensuring no exception is thrown besides allocation failure (that's pretty much fatal, isn't it? So...), and return a simple C-string.
With typedefs you can get some weird results, like this (for some reason, the site breaks the formatting of this snippet, so I'm using this paste link): http://pastebin.com/f51b888ad
Despite those disadvantages, I'd like to say that it sure is fast. For the second time you lookup for one same type name, it will cost picking a reference to a global std::string containing the name. And, comparatively to the template specialiazation methods suggested before, there is nothing else you have to declare besides the very template itself, so it is really much easier to use.
No, you cannot work on types as if they were variables. You could write code that extracted the typeid() of an element and printed the name, but the resulting value will probably not be what you expect (type names are not standarized).
You can also work with template specializations (and some macro magic) to achieve a more interesting version if the number of types you want to work with is limited:
template <typename T> const char* printtype(); // not implemented
// implement specializations for given types
#define DEFINE_PRINT_TYPE( type ) \
template<>\
const char* printtype<type>() {\
return #type;\
}
DEFINE_PRINT_TYPE( int );
DEFINE_PRINT_TYPE( double );
// ... and so on
#undef DEFINE_PRINT_TYPE
template <typename T> void test()
{
std::cout << printtype<T>() << std::endl;
}
int main() {
test<int>();
test<double>();
test<float>(); // compilation error, printtype undefined for float
}
Or you could even combine both versions: implement the printtype generic template using typeinfo and then provide specializations for the types you want to have fancier names.
template <typename T>
const char* printtype()
{
return typeid(T).name();
}
This breaks one of my primary tenets of C++ code writing: Avoid using tricks in both the template features and the preprocessor at the same time.
Part of the reason for templates and the nastiness they introduce into the language was an attempt to wean developers away from using the preprocessor. If you use both, then the terrorists win.
If you use boost/core/demangle.hpp, you can get a reliable human-readable string.
char const * name = typeid(T).name();
boost::core::scoped_demangled_name demangled( name );
std::cout << (demangled.get() ? demangled.get() : "Failed to demangle") << std::endl;
in my code I use the "awful" double-declaration of the "Class-Name"
MqFactoryC<MyServer>::Add("MyServer").Default();
because c++ is NOT able to extract the string "MyServer" from the template…
the only "way" to get "rid" of this… using a cpp "wrapper"
#define MQ_CPPSTR(s) #s
#define MqFactoryCAdd(T) MqFactoryC<T>::Add(MQ_CPPSTR(T)).Default()
Here’s what I do: I have a demangle() function (implemented on top of abi::__cxa_demangle() which I call with a couple of convenience template function overloads, nameof(), with either the type I want stringified or an instance of same.
It’s fairly compact, so I’ll reproduce it here in all its glory. In demangle.hh we have:
#pragma once
#include <typeinfo>
namespace terminator {
/// actual function to demangle an allegedly mangled thing
char const* demangle(char const* const symbol) noexcept;
/// convenience function template to stringify a name of a type,
/// either per an explicit specialization:
/// char const* mytypename = terminator::nameof<SomeType>();
template <typename NameType>
char const* nameof() {
try {
return demangle(typeid(NameType).name());
} catch (std::bad_typeid const&) {
return "<unknown>";
}
}
/// … or as implied by an instance argument:
/// char const* myinstancetypename = terminator::nameof(someinstance);
template <typename ArgType>
char const* nameof(ArgType argument) {
try {
return demangle(typeid(argument).name());
} catch (std::bad_typeid const&) {
return "<unknown>";
}
}
} /* namespace terminator */
… And then in demangle.cpp:
#include "demangle.hh"
#include <cstdlib>
#include <cxxabi.h>
#include <mutex>
#include <memory>
namespace terminator {
namespace {
/// define one singular, private, static std::mutex,
/// to keep the demangler from reentering itself
static std::mutex mangle_barrier;
/// define a corresponding private and static std::unique_ptr,
/// using a delete-expression to reclaim the memory malloc()'ed by
/// abi::__cxa_demangle() upon its return.
/// … we use clang pragmas to add flags locally for this to work:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr<char, decltype(std::free)&> demangled_name{ nullptr, std::free };
#pragma clang diagnostic pop
}
char const* demangle(char const* const symbol) noexcept {
if (!symbol) { return "<null>"; }
std::lock_guard<std::mutex> lock(mangle_barrier);
int status = -4;
demangled_name.reset(
abi::__cxa_demangle(symbol,
demangled_name.get(),
nullptr, &status));
return ((status == 0) ? demangled_name.release() : symbol);
}
} /* namespace terminator */
To use this, I think you’ll have to link to libc++ (or whatever your local equivalent is) to use abi::__cxa_demangle(). What may be suboptimal for the OP is the fact that this does the demangling and stringification at runtime. I’d personally love something constexpr-friendly in leu of this, but since I suffer from a severe macro-abuse allergy, I find this to be the least generally-unreasonable solution to this problem.
(the terminator namespace is inconsequential – I use this code in a libunwind-based stacktracer called from termination handler – feel free to s///g that token)