一、获取类型名称
在前面的反射中,可以通过一些技术手段来实现获取类型的名称。这么一个看似简单的功能,其实实现起来并没有想象的那么简单。在一些框架中,包含了类似的功能,而主流的编译器则支持的各有不同。这不是说这种接口有多么难,而各个编译器可能有自己的实际的出发点或者说应用点,所以才没有做到完全与开发者想象的相同。
编译器厂商的不同造成的结果有点类似于世界上的同语种但不同语言的民族一样,明明大家都明白那个意思,但就是呈现出来不一样。
二、库的支持
在前面也提到过,有一种标准库提到的运算符即typeid(type or expression),它包含在头文件中 中。此处不重点讲解这个运算符的应用,只是提醒大家,这个运算符在不同的编译平台上,产生的结果是不尽相同的,这个就比较麻烦了。可以试着在不同的平台编译一下以下的代码:
auto iname = typeid(int).name();
在常见的VC编译器和g++编译器中得到结果是:
//vc
iname :int
//g++
iname:i
包括在Clang中,也和g++一样产生类似的效果。那么有没一种方法,让二者产生一种更接近于人们的普遍认知的结果呢?肯定有。其实也很好理解,这是处理C++编译器的重命名的问题,也就是人们常说的mangle和demangle现象。那么在gcc中提供了:
#ifdef __cplusplus
namespace __cxxabiv1
{
extern "C"
{
#endif
char* __cxa_demangle(const char* __mangled_name, char* __output_buffer,
size_t* __length, int* __status);
#ifdef __cplusplus
}
} // namespace __cxxabiv1
#endif
虽然它可以得到类似于VC平台的差不多相近的结果,但实际还是有一些细节的差别的。请在下面的例程中认真的体验。
三、例程
可以将例程在VC和gcc中进行对比查看:
class Teacher
{};
int main() {
auto iname = typeid(int).name();
std::cout << "iname type name:" << iname << std::endl;
// struct or class
iname = typeid(Teacher).name();
std::cout << "iname type name:" << iname << std::endl;
//VC下运行需要注释掉
iname = abi::__cxa_demangle(typeid(Teacher).name(), nullptr, nullptr, nullptr);
std::cout << "iname type name:" << iname << std::endl;
// array
Teacher t[10];
iname = typeid(t).name();
std::cout << "iname type name:" << iname << std::endl;
iname = abi::__cxa_demangle(typeid(t).name(), nullptr, nullptr, nullptr);
std::cout << "iname type name:" << iname << std::endl;
// cv
Teacher ct;
const Teacher &act = ct;
iname = typeid(act).name();
std::cout << "iname type name:" << iname << std::endl;
iname = abi::__cxa_demangle(typeid(act).name(), nullptr, nullptr, nullptr);
std::cout << "iname type name:" << iname << std::endl;
return 0;
}
运行结果:
//gnuc
iname type name:i
iname type name:7Teacher
iname type name:Teacher
iname type name:A10_7Teacher
iname type name:Teacher [10]
iname type name:7Teacher
iname type name:Teacher
//vc-处理相关的函数后
iname type name:int
iname type name:class Teacher
iname type name:class Teacher
iname type name:class Teacher [10]
iname type name:class Teacher [10]
iname type name:class Teacher
iname type name:class Teacher
在typeid的说明中,已经说明了,其已经自动去除了cv限定符,所以如果需要得到类型包含这个限定符,就有问题了。基础的应用就是基础的应用,不能强求太多的要求。有的时候儿在一些开源库中看到大堆的模板和宏处理这类问题,就知道,解决这个不起眼的小功能,可能就需要很多工作量和代码。
四、总结
其实今天分析的这个现象,再次验证了标准是标准,实现是实现这个道理。标准指挥着实现,但实现不一定必须严格按标准来。有些标准未要求到的或者说对实现手段和结果没有要求的,这都是编译器厂商可以自行决定的。包括在实现标准后再在其基础上进行扩展支持的,这都不少见。
标准是方向和目标,编译厂商是行动者和实践者,达到目的不一定非得走一条路。