C++ C++;获取模板中类型的名称
我正在编写一些用于解析某些文本数据文件的模板类,因此,大多数解析错误可能是由于数据文件中的错误造成的,这些错误大部分不是由程序员编写的,因此需要一条关于应用程序未能加载原因的好消息,例如:C++ C++;获取模板中类型的名称,c++,templates,compile-time,typename,C++,Templates,Compile Time,Typename,我正在编写一些用于解析某些文本数据文件的模板类,因此,大多数解析错误可能是由于数据文件中的错误造成的,这些错误大部分不是由程序员编写的,因此需要一条关于应用程序未能加载原因的好消息,例如: template<typename T> struct TypeParseTraits; #define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \ { static const c
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...
分析example.txt时出错。[MySectiom]键的值(“notaninteger”)不是有效的int
我可以根据传递给模板函数的参数和类中的成员变量计算出文件名、节名和键名,但是我不确定如何获取模板函数试图转换为的类型的名称
我当前的代码看起来像是,专门针对纯字符串之类的:
template<typename T> T GetValue(const std::wstring §ion, 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));
}
}
模板T GetValue(常量std::wstring§ion,常量std::wstring&key)
{
std::map::iterator it=map[section].find(key);
if(it==map[section].end())
throw ItemDoesNotExist(文件、节、键)
其他的
{
试试{return boost::lexical_cast(it->second);}
//需要从T那里得到这个名字
catch(…)抛出ParseError(文件、节、键、it->second、TypeName(T));
}
}
我宁愿不必对数据文件可能使用的每种类型都进行特定的重载,因为有很多类型的数据文件
此外,我需要一个解决方案,除非发生异常,否则不会产生任何运行时开销,也就是说,我想要一个完全编译时的解决方案,因为这段代码被调用了很多次,加载时间已经有点长了
编辑:好,这是我提出的解决方案:
我有一个类型。h包含以下内容
#pragma once
template<typename T> const wchar_t *GetTypeName();
#define DEFINE_TYPE_NAME(type, name) \
template<>const wchar_t *GetTypeName<type>(){return name;}
#pragma一次
模板常量wchar_t*GetTypeName();
#定义类型名称(类型、名称)\
templateconst wchar\u t*GetTypeName(){return name;}
然后,我可以使用DEFINE_TYPE_NAME宏在cpp文件中为我需要处理的每种类型(例如,在定义了要开始的类型的cpp文件中)定义一个名称
然后,链接器就可以找到模板专门化,只要它是在某处定义的,或者抛出链接器错误,这样我就可以添加类型。解决方案是
typeid(T).name()
返回。Jesse Beder的解决方案可能是最好的,但如果您不喜欢typeid给您的名称(例如,我认为gcc给您的名称有误),您可以执行以下操作:
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...
模板
结构类型分析特征;
#定义寄存器解析类型(X)模板结构类型解析特征\
{static const char*name;};const char*TypeParseTraits::name=#X
寄存器类型(int);
寄存器\解析\类型(双);
寄存器\解析\类型(FooClass);
//等等。。。
然后像这样使用它
throw ParseError(TypeParseTraits<T>::name);
throw ParseError(TypeParseTraits::name);
编辑:
您还可以将这两者结合起来,将name
更改为一个默认调用typeid(T).name()
的函数,然后只针对不可接受的情况进行专门化。typeid(T).name()
是实现定义的,不保证人类可读的字符串
阅读:
返回实现定义的以null结尾的字符串
包含类型的名称。没有提供任何保证,以
特别是,对于几种类型和类型,返回的字符串可以相同
在同一程序的调用之间进行更改
使用gcc和clang等编译器,返回的字符串可以通过c++filt-t管道传输,以转换为人类可读的形式
但在某些情况下,gcc不会返回正确的字符串。例如,在我的机器上,我有一个带有-std=c++11
的gcc,并且内部有模板函数typeid(T).name()
为“unsigned int”
返回“j”
。这就是所谓的破损名称。要获取真实的类型名,请使用
功能(仅gcc):
#包括
#包括
#包括
模板
std::字符串类型_name()
{
智力状态;
std::string tname=typeid(T).name();
char*demangled_name=abi::_cxa_demangle(tname.c_str(),NULL,NULL和status);
如果(状态==0){
tname=所需的名称;
标准::免费(要求的名称);
}
返回tname;
}
如Bunkar typeid(T)所述。名称由实现定义
要避免此问题,可以使用库
例如:
boost::typeindex::type_id<T>().pretty_name() // human readable
boost::typeindex::type_id().pretty_name()//人类可读
重新表述安德烈的回答:
该库可用于打印类型名称
在模板中,这可能如下所示
#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;
}
#包括
#包括
模板
void printNameOfType(){
洛根·卡帕尔多的答案是正确的,但可以稍微简化,因为不需要每次专门化这个类。可以这样写:
// 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...
//在标题中
模板
结构类型分析特征
{static const char*name;};
//在c文件中
#定义寄存器解析类型(X)\
模板常量char*TypeParseTraits::name=#X
寄存器类型(int);
寄存器\解析\类型(双);
寄存器\解析\类型(FooClass);
//等等。。。
这也允许您将ReistSyPARSETYPE类型的指令放在C++文件中…
我就把它放在那里。
如果有人仍然需要它,那么您可以使用:
template <class T>
bool isString(T* t) { return false; } // normal case returns false
template <>
bool isString(char* t) { return true; } // but for char* or String.c_str() returns true
.
.
.
模板
bool-isString(T*T){return false;}//正常情况下返回false
模板
bool isString(char*t){return true;}//但是对于char*或String.c_str()返回true
.
.
.
这将只检查类型而不获取它,并且只检查1个类型或2个类型。如果您想要一个漂亮的名称,Logan Capaldo的解决方案无法处理复杂的数据结构:REGISTER\u PARSE\u type(map)
和typeid(map).name()
为我提供了st3mapiiist4lessiiesaist4pairikieee
另一个有趣的答案是使用unordered_map
或map
得出的
#包括
#包括
#包括
#包括
使用名称空间std;
无序映射类型映射;
int main(){
类型映射[typeid(int)]=“int”;
#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;
int main(){
types_map_[typeid(int)]="int";
types_map_[typeid(float)]="float";
types_map_[typeid(map<int,int>)]="map<int,int>";
map<int,int> mp;
cout<<types_map_[typeid(map<int,int>)]<<endl;
cout<<types_map_[typeid(mp)]<<endl;
return 0;
}
template <typename T> const char *foo()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
std::cout << TypeName<float>() << '\n';
std::cout << TypeName(1.2f); << '\n';
#include <array>
#include <cstddef>
namespace impl
{
template <typename T>
constexpr const auto &RawTypeName()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
struct RawTypeNameFormat
{
std::size_t leading_junk = 0, trailing_junk = 0;
};
// Returns `false` on failure.
inline constexpr bool GetRawTypeNameFormat(RawTypeNameFormat *format)
{
const auto &str = RawTypeName<int>();
for (std::size_t i = 0;; i++)
{
if (str[i] == 'i' && str[i+1] == 'n' && str[i+2] == 't')
{
if (format)
{
format->leading_junk = i;
format->trailing_junk = sizeof(str)-i-3-1; // `3` is the length of "int", `1` is the space for the null terminator.
}
return true;
}
}
return false;
}
inline static constexpr RawTypeNameFormat format =
[]{
static_assert(GetRawTypeNameFormat(nullptr), "Unable to figure out how to generate type names on this compiler.");
RawTypeNameFormat format;
GetRawTypeNameFormat(&format);
return format;
}();
}
// Returns the type name in a `std::array<char, N>` (null-terminated).
template <typename T>
[[nodiscard]] constexpr auto CexprTypeName()
{
constexpr std::size_t len = sizeof(impl::RawTypeName<T>()) - impl::format.leading_junk - impl::format.trailing_junk;
std::array<char, len> name{};
for (std::size_t i = 0; i < len-1; i++)
name[i] = impl::RawTypeName<T>()[i + impl::format.leading_junk];
return name;
}
template <typename T>
[[nodiscard]] const char *TypeName()
{
static constexpr auto name = CexprTypeName<T>();
return name.data();
}
template <typename T>
[[nodiscard]] const char *TypeName(const T &)
{
return TypeName<T>();
}
#define DECLARE_SET_FORMAT_FOR(type) \
if ( typeid(type) == typeid(T) ) \
formatStr = #type;
template<typename T>
static std::string GetFormatName()
{
std::string formatStr;
DECLARE_SET_FORMAT_FOR( uint8_t )
DECLARE_SET_FORMAT_FOR( int8_t )
DECLARE_SET_FORMAT_FOR( uint16_t )
DECLARE_SET_FORMAT_FOR( int16_t )
DECLARE_SET_FORMAT_FOR( uint32_t )
DECLARE_SET_FORMAT_FOR( int32_t )
DECLARE_SET_FORMAT_FOR( float )
// .. to be exptended with other standard types you want to be displayed smartly
if ( formatStr.empty() )
{
assert( false );
formatStr = typeid(T).name();
}
return formatStr;
}