是否可以从 C++ 中的模板类型获取 char* 名称
题
我想获取模板类型的字符串名称(const char*)。不幸的是我无法访问 RTTI。
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return /* magic goes here */; }
}
所以
SomeClass<int> sc;
sc.GetClassName(); // returns "int"
这可能吗?我找不到办法,准备放弃。谢谢您的帮助。
解决方案
没有,它不会与工作的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) { ... }
升压已经有这样的模板,并且下一个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();
当然,你也可以摆脱主模板定义的(并且只保留了向前声明)如果你想如果类型是不知道得到一个编译时错误。我只是包括在这里完成。
我用字符常量的静态数据成员*前面,但他们会导致一些复杂的问题,这样的问题在那里把他们的声明,等等。班特像上面轻松地解决这个问题。
自动,取决于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
魔术会给你你查找字符串表示。
其他提示
真正简单的解决方法:只是传递一个字符串对象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;
你可以尝试这样的事情(警告这只是我的想法,所以可能会出现编译错误等......)
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.邦迪的回答或者德克特利的回答都会起作用。除了我的回答之外,您还可以在三个不同的位置添加该信息:
- 在对象创建时使用
SomeClass<int>("int")
- 在类中使用dirkgently的编译时RTTI或虚函数
- 使用我的解决方案的模板。
这三者都可以工作,问题只是在你的情况下,你最终会在哪里遇到最少的维护麻烦。
这是你没有访问RTTI,这是否意味着你不能使用的typeid(T)。名称()?因为这几乎是编译器的帮助下做到这一点的唯一方法。
这是非常重要的类型有唯一的名字,或者会以某种方式持续的名字?如果是这样,你应该考虑给他们的东西比在代码中声明的只是类的名称更稳健。你可以把它们放在不同的命名空间给两个类相同的不合格的名称。你也可以把两个类具有相同名称(包括名称空间限定)在Windows中两个不同的DLL文件,所以您需要将DLL识别的被包括在名称为好。
这一切都取决于你打算做琴弦,当然什么。
您可以自己添加一个小魔术。是这样的:
#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
)。
没有,对不起。
如果您尝试使用它INT RTTI甚至不会编译。