Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;获取模板中类型的名称_C++_Templates_Compile Time_Typename - Fatal编程技术网

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 &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));
    }
}
模板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;
}