10

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

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

所以

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

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

4

7 回答 7

13

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

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

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<char, std::char_traits<char>, std::allocator<char> >]

一些substr混合的魔法find会给你你寻找的字符串表示。

于 2009-02-27T19:53:10.207 回答
3

真正简单的解决方案:只需将一个字符串对象传递给 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;
于 2009-02-27T19:23:16.893 回答
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<int>("int")
  2. 在使用 dirkgently 的编译时 RTTI 或虚函数的类中
  3. 使用我的解决方案的模板。

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

于 2009-02-27T19:33:25.590 回答
1

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

于 2009-02-27T19:15:52.170 回答
1

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

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

于 2009-02-27T21:14:14.367 回答
0

你可以自己添加一点魔法。就像是:

#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)。

于 2009-02-27T19:16:48.563 回答
-1

不,对不起。

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

于 2009-02-27T19:15:00.033 回答