C ++获取模板中的types名称

我正在写一些模板类来parsing一些文本数据文件,就像这样,绝大多数parsing错误是由于数据文件中的错误造成的,而这些错误大部分都不是程序员写的,所以需要一个很好的消息,为什么应用程序未能加载,如:

parsingexample.txt时出错。 [MySectiom]键的值(“notaninteger”)不是有效的int

我可以从传递给模板函数和类成员variables的参数中创build文件,节和键名称,但是我不知道如何获取模板函数试图转换的types的名称。

我目前的代码看起来像专门为简单的string和这样的:

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

而不必为数据文件可能使用的每种types制定特定的重载,因为它们有负载…

此外,我需要一个解决scheme,不会产生任何运行时间的开销,除非发生exception,即完全编译时解决scheme是我想要的,因为这个代码被称为吨的时间和加载时间已经有点长。

编辑:好吧,这是我想出的解决scheme:

我有一个types.h包含以下内容

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

然后,我可以使用DEFINE_TYPE_NAMEmacros在cpp文件中处理每个需要处理的types(例如在定义types的cpp文件中)。

只要连接器在某处被定义,连接器就能够find合适的模板专门化,否则就会抛出链接器错误,以便我可以添加该types。

杰西·比德的解决scheme可能是最好的,但如果你不喜欢typeid给你的名字(我认为gcc给你例如mangled的名字),你可以做这样的事情:

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

然后像使用它

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

编辑:

你也可以把两者结合起来,把name改成默认调用typeid(T).name()的函数,然后只针对那些不可接受的情况。

运行时解决scheme是

 typeid(T).name() 

它返回一个(依赖于编译器的,我相信)所讨论types的名称。 它不会被调用,除非抛出一个exception(在你的代码中),所以它可能会满足你的标准。

typeid(T).name()是实现定义的,不保证可读的string。

阅读cppreference.com :

返回一个实现定义的以Null结尾的包含types名称的string。 没有给出任何保证,特别是返回的string对于几种types可以是相同的,并且在相同程序的调用之间改变。

使用gcc和clang等编译器,返回的string可以通过c ++ filt -t进行pipe道转换为可读的forms。

但在某些情况下,gcc不会返回正确的string。 例如在我的机器上,我有gcc whith -std=c++11 ,内部模板函数typeid(T).name()返回"j""unsigned int" 。 这就是所谓的错位的名字。 要获得真实的types名称,请使用abi :: __ cxa_demangle()函数(仅限gcc):

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

正如Bunkar提到的,typeid(T).name是实现定义的。

为了避免这个问题,你可以使用Boost.TypeIndex库。

例如:

 boost::typeindex::type_id<T>().pretty_name() // human readable 

作为安德烈回答的一个改写:

Boost TypeIndex库可用于打印types的名称。

在模板内部,这可能如下所示

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

Logan Capaldo的答案是正确的,但可以稍微简化一下,因为不必每次都专业化。 可以写:

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

这也允许你把REGISTER_PARSE_TYPE指令放在C ++文件中