【问题标题】:Is it possible to get a char* name from a template type in C++是否可以从 C++ 中的模板类型中获取 char* 名称
【发布时间】:2010-10-10 10:02:31
【问题描述】:

我想获取模板类型的字符串名称(const char*)。很遗憾,我无法访问 RTTI。

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return /* magic goes here */; }
}

所以

SomeClass<int> sc;
sc.GetClassName();   // returns "int"

这可能吗?我找不到方法,准备放弃。感谢您的帮助。

【问题讨论】:

  • 这就是 typeid 的用途。为什么不能用?
  • 你能告诉我们你为什么需要它吗?也许有更好的用途?我强烈建议你重构程序,这样你就不再需要字符串了。
  • 这些类表示磁盘上的数据。我希望能够通过实用程序检查数据并获得类的名称。我还想使用字符串名称来创建一个哈希来识别类型(在磁盘上)。它在技术上不是必需的,但很高兴拥有。
  • 这里typeid的使用不需要RTTI。

标签: c++ string templates typeid


【解决方案1】:

不,它也不能与 typeid 一起使用。它将为您提供一些取决于编译器实现的内部字符串。 int 常用“int”之类的东西,但“i”也很常见。

顺便说一句,如果你只想比较两种类型是否相同,你不需要先将它们转换为字符串。你可以这样做

template<typename A, typename B>
struct is_same { enum { value = false }; };

template<typename A>
struct is_same<A, A> { enum { value = true }; };

然后做

if(is_same<T, U>::value) { ... }

Boost 已经有了这样的模板,下一个 C++ 标准也将有 std::is_same

手动注册类型

您可以专注于以下类型:

template<typename> 
struct to_string {
    // optionally, add other information, like the size
    // of the string.
    static char const* value() { return "unknown"; }
};

#define DEF_TYPE(X) \
    template<> struct to_string<X> { \
        static char const* value() { return #X; } \
    }

DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...

所以,你可以像这样使用它

char const *s = to_string<T>::value();

当然,如果你想在类型未知的情况下出现编译时错误,你也可以去掉主模板定义(只保留前向声明)。我只是将其包含在此处以完成。

我以前使用过 char const* 的静态数据成员,但它们会导致一些复杂的问题,例如在哪里放置它们的声明等问题。像上面这样的类专业化很容易解决这个问题。

自动,取决于 GCC

另一种方法是依赖编译器内部。在 GCC 中,以下给出了合理的结果:

template<typename T>
std::string print_T() {
    return __PRETTY_FUNCTION__;
}

返回std::string

std::string print_T() [with T = std::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;]

一些substrfind 混合的魔法将为您提供所需的字符串表示。

【讨论】:

  • 不做类型相等。我尝试了编译器特定的方法,但我使用的是 MS(等效不起作用)。第二种方法很好,非侵入性的。我正在使用其他一些宏,所以这可以很容易地工作。如果你还没有定义你正在使用的类型,我也可以做一个编译时断言。
  • FUNCTION 对 MS 编译器有帮助吗? (我没有要测试的。)参考:stackoverflow.com/questions/597078/…
  • mrree,很抱歉我没有测试过 FUNCTION 的样子。如果它只打印函数的名称,我们就迷路了。如果它也打印它得到的模板参数,我们不是:) 最好测试它,请报告你的结果。谢谢:)
  • 我喜欢你的DEF_TYPE;我从来没有想过在模板的帮助下可以避免过多的宏#include 诡计。
【解决方案2】:

真正简单的解决方案: 只需将一个字符串对象传递给 SomeClass 的构造函数,它说明类型是什么。

例子:

#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));

只需在GetClassName的实现中存储并显示。

稍微复杂一点的解决方案,但仍然很简单:

#define DEC_SOMECLASS(T, name) SomeClass<T> name;  name.sType = #T; 

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return sType.c_str(); }
    std::string sType;
};


int main(int argc, char **argv)
{
    DEC_SOMECLASS(int, s);
    const char *p = s.GetClassName();

    return 0;
}

模板非类型解决方案:

您还可以创建自己的类型 ID,并具有在 ID 和字符串表示形式之间进行转换的函数。

那么就可以在将类型声明为模板非类型参数时传递ID:

template< typename T, int TYPEID>
struct SomeClass
{
    const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};


...

SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;

【讨论】:

  • 不幸的是,这是不可能的。您不能将字符串文字传递给模板
  • 是的,我发现我现在正在编写一个新的解决方案:)
  • DEC_SOMECLASS 实际上是一个对象的定义 :)
  • 是的,它是声明 SomeClass 类的对象的缩写。
  • 我不想在类中添加实例数据,因为它表示磁盘外的数据,静态数据就可以了。此外,解决方案需要在声明时完成。我在其他类中声明我需要从模板类型中获取文本名称的类。抱歉,我没有更具体。
【解决方案3】:

您可以尝试这样的事情(警告这只是我的想法,所以可能会出现编译错误等。)

template <typename T>
const char* GetTypeName()
{
    STATIC_ASSERT(0); // Not implemented for this type
}

#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }

// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return GetTypeName<T>; }
}

这适用于您为其添加GETTYPENAME(type) 行的任何类型。它的优点是无需修改您感兴趣的类型即可工作,并且可以使用内置类型和指针类型。它确实有一个明显的缺点,即您必须为每种要使用的类型设置一行。

如果不使用内置 RTTI,您将不得不自己在某处添加信息,无论是 Brian R. Bondy 的答案还是 dirkgently 的都可以。除了我的回答之外,您还可以在三个不同的位置添加该信息:

  1. 在对象创建时使用SomeClass&lt;int&gt;("int")
  2. 在使用 dirkgently 的编译时 RTTI 或虚函数的类中
  3. 使用我的解决方案使用模板。

这三个都可以工作,只是在你的情况下你会在哪里得到最少的维护麻烦。

【讨论】:

  • 这是如何工作的?我无法让它工作。特别是如果有人可以解释#define GETTYPENAME(x) str(x) 位,那就太好了。 str(x) 不是 STR(x)。这是什么?
【解决方案4】:

你无权访问 RTTI,是否意味着你不能使用 typeid(T).name()?因为这几乎是在编译器的帮助下完成它的唯一方法。

【讨论】:

  • 相当肯定 typeid 只有在 RTTI 开启时才会出现,所以没有 typeid。
  • 严格来说RTTI还包括dynamic_cast,如果这是用/GR编译的MSVC,你仍然可以使用typeid,你只是不能在变量上使用它并期望得到正确的结果。不过,您可以对类型感到满意。
【解决方案5】:

类型具有唯一名称是否非常重要,或者名称会以某种方式持久化?如果是这样,你应该考虑给他们一些比代码中声明的类名更健壮的东西。您可以通过将两个类放在不同的名称空间中来为它们赋予相同的非限定名称。您还可以在 Windows 上将两个具有相同名称(包括命名空间限定)的类放在两个不同的 DLL 中,因此您还需要在名称中包含 DLL 的标识。

当然,这完全取决于你要对字符串做什么。

【讨论】:

    【解决方案6】:

    您可以自己添加一点魔法。比如:

    #include <iostream>
    
    #define str(x) #x
    #define xstr(x) str(x)
    #define make_pre(C) concat(C, <)
    #define make_post(t) concat(t, >)
    
    #define make_type(C, T) make_pre(C) ## make_post(T)
    #define CTTI_REFLECTION(T, x)  static std::string my_typeid() \
                                   { return xstr(make_type(T, x)); }
    
    
    // the dark magic of Compile Time Type Information (TM)
    #define CTTI_REFLECTION(x)  static const char * my_typeid() \
                                      { return xstr(make_type(T, x)); }
    
    #define CREATE_TEMPLATE(class_name, type) template<> \
                                        struct class_name <type>{ \
                                            CTTI_REFLECTION(class_name, type) \
                                        }; 
    
    // dummy, we'll specialize from this later
    template<typename T> struct test_reflection;
    
    // create an actual class
    CREATE_TEMPLATE(test_reflection, int)
    
    struct test_reflection {
      CTTI_REFLECTION(test_reflection)
    };
    
    int main(int argc, char* argv[])
    {
        std::cout << test_reflection<int>::my_typeid();
    }
    

    我将使检查器成为static 函数(因此非const)。

    【讨论】:

      【解决方案7】:

      不,抱歉。

      如果你尝试在 int 上使用 RTTI,它甚至不会编译。

      【讨论】:

      • 一个人不知道。据我所知,标准没有可用的“没有 RTTI 的托管 c++ 实现”选项。所以人们必须猜测或不说什么:)
      猜你喜欢
      • 2018-06-22
      • 2010-11-06
      • 2017-04-30
      • 1970-01-01
      • 2013-01-24
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多