C ++ Get type name in template

I am writing several template classes for parsing some text data files, and, like most of them, most parsing errors will be due to errors in the data file, which are mostly not written by programmers, and therefore I need a good message about why the application failed to load, for example something like:

Error analysis in the .txt example. The value ("notaninteger") of the [MySectiom] key is not a valid int value

I can process files, sections, and key names from the arguments passed to the template function and member classes in the class, however I'm not sure how to get the name of the type that the template function is trying to convert to.

My current code looks, with specializations for simple strings, etc.

template<typename T> T GetValue(const std::wstring &section, const std::wstring &key) { std::map<std::wstring, std::wstring>::iterator it = map[section].find(key); if(it == map[section].end()) throw ItemDoesNotExist(file, section, key) else { try{return boost::lexical_cast<T>(it->second);} //needs to get the name from T somehow catch(...)throw ParseError(file, section, key, it->second, TypeName(T)); } } 

An identifier probably should not do specific overloads for each type that data files can use, since there are many of them ...

I also need a solution that does not incur any overhead at runtime unless an exception occurs, i.e. a fully compiled workaround is what I want, as this code is called tons of times, and the load time is already slightly longer.

EDIT: Ok, this is the solution I came across:

I have .h types containing the following

 #pragma once template<typename T> const wchar_t *GetTypeName(); #define DEFINE_TYPE_NAME(type, name) \ template<>const wchar_t *GetTypeName<type>(){return name;} 

Then I can use the DEFINE_TYPE_NAME macro in cpp files for each type that I need to deal with (for example, in a cpp file that defines the type to start).

The linker can then find a suitable template specialization if it is defined somewhere, or throw a linker error otherwise so that I can add this type.

+54
c ++ templates compile-time typename
Jun 28 '09 at 18:30
source share
6 answers

Jesse Beder's solution is most likely the best, but if you don't like the typeid names you give (I think gcc gives you malformed names, for example), you can do something like:

 template<typename T> struct TypeParseTraits; #define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \ { static const char* name; } ; const char* TypeParseTraits<X>::name = #X REGISTER_PARSE_TYPE(int); REGISTER_PARSE_TYPE(double); REGISTER_PARSE_TYPE(FooClass); // etc... 

And then use it like

 throw ParseError(TypeParseTraits<T>::name); 

EDIT:

You can also combine the two, change name as a function that by default calls typeid(T).name() , and then specializes only in cases where this is unacceptable.

+32
Jun 28 '09 at 19:21
source share

Runtime solution

 typeid(T).name() 

which returns (compiler dependent, I believe) the name of the type in question. It will not be called if an exception is not thrown (in your code), so it can satisfy your criteria.

+46
Jun 28 '09 at 18:42
source share

typeid(T).name() is implementation-defined and does not guarantee a user-readable string.

Reading cppreference.com :

Returns a specific implementation of a character string with a null character containing the type name. No guarantees are provided, in particular, the returned string may be the same for several types and the change between calls to the same program.

...

With compilers such as gcc and clang, the returned string can be passed through C ++ filter -t to convert it to a human-readable form.

But in some cases, gcc does not return the correct string. For example, on my machine, I have gcc whith -std=c++11 , and inside the template function typeid(T).name() returns "j" for "unsigned int" . This is the so-called perverted name. To get the real type name, use the abi :: __ cxa_demangle () function (gcc only):

 #include <string> #include <cstdlib> #include <cxxabi.h> template<typename T> std::string type_name() { int status; std::string tname = typeid(T).name(); char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status); if(status == 0) { tname = demangled_name; std::free(demangled_name); } return tname; } 
+30
01 Oct '13 at 18:59
source share

As mentioned in Bunkar typeid (T) .name, an implementation is defined.

To avoid this problem, you can use the Boost.TypeIndex library .

For example:

 boost::typeindex::type_id<T>().pretty_name() // human readable 
+13
Feb 26 '15 at 9:22
source share

How to paraphrase Andrey's answer:

The Boost TypeIndex library can be used to print type names.

Inside the template, it might look like this

 #include <boost/type_index.hpp> #include <iostream> template<typename T> void printNameOfType() { std::cout << "Type of T: " << boost::typeindex::type_id<T>().pretty_name() << std::endl; } 
+6
Oct 26 '15 at 3:06 on
source share

Logan Capaldo's answer is correct, but it can be slightly simplified because there is no need to specialize in the class every time. You can write:

 // in header template<typename T> struct TypeParseTraits { static const char* name; }; // in c-file #define REGISTER_PARSE_TYPE(X) \ template <> const char* TypeParseTraits<X>::name = #X REGISTER_PARSE_TYPE(int); REGISTER_PARSE_TYPE(double); REGISTER_PARSE_TYPE(FooClass); // etc... 

It also allows you to place REGISTER_PARSE_TYPE statements in a C ++ file ...

+4
Apr 28 '16 at 21:58
source share



All Articles