any 的实现
我们简单的研究一下 boost.any 的实现。如果我们简单的记录一个 void* 指针,其实没有把相关的 typeinfo 记录下来。而 any_cast 的动态转换特性也使得我们考虑使用别的机制。事实上,boost.any 实现的时候依赖了下面这种结构:
class placeholder {
public:
virtual ~placeholder() {}
virtual const std::type_info & type() const = 0;
virtual placeholder * clone() const = 0;
} ;
template<typename ValueType>
class holder : public placeholder {
public:
holder(const ValueType & value) : held(value) {}
virtual const std::type_info & type() const {
return typeid(ValueType) ;
}
virtual placeholder * clone() const {
return new holder(held);
}
ValueType held;
private: // intentionally left unimplemented
holder & operator=(const holder &);
};
通过这个结构在 holder 里面存放不同的数据,而且通过父类中调用的虚方法 type() 获得对应的 typeinfo 保证类型安全。而 any 的实现如下:
namespace boost {
class any {
public:
any() : content(0) {}
template<typename ValueType>
any(const ValueType & value) : content(new holder<ValueType>(value)) {}
any(const any & other) : content(other.content ? other.content->clone() : 0) {}
~any() { delete content; }
any & swap(any & rhs) {
std::swap(content, rhs.content);
return *this;
}
template<typename ValueType>
any & operator=(const ValueType & rhs) {
any(rhs).swap(*this);
return *this;
}
any & operator=(any rhs) {
rhs.swap(*this);
return *this;
}
bool empty() const { return !content; }
const std::type_info & type() const { return content ? content->type() : typeid(void); }
private:
template<typename ValueType>
friend ValueType * any_cast(any *);
template<typename ValueType>
friend ValueType * unsafe_any_cast(any *);
placeholder * content;
} ;
}
这样,我们需要的大部分功能就可以实现了,另外需要定义一些 exception 就差不多了。
一般编译器如何实现 RTTI 呢?如果一个 class 没有 virtual 方法,那么使用 typeid 可以直接根据类型(不论是指针、还是引用)获得对应的 typeinfo 对象(由编译器统一负责维护,返回的名字也是编译器自己决定的,可以参考后面的例程),如果有虚函数,那么对象存在 vtab,可以在虚表里面加入指向 typeinfo 对象的指针,这样就可以返回对应的 typeinfo 对象了。下面是一个展示 typeinfo 的例子(来自这里):
#include <iostream>
#include <typeinfo>
using namespace std;
struct Base {};
struct Derived : Base {};
struct Poly_Base {virtual void Member(){}};
struct Poly_Derived: Poly_Base {};
int
main( int argc, char* argv[] ) {
int i;
int * pi;
cout << "int is: " << typeid(int).name() << endl;
cout << " i is: " << typeid(i).name() << endl;
cout << " pi is: " << typeid(pi).name() << endl;
cout << "*pi is: " << typeid(*pi).name() << endl << endl;
Derived derived;
Base* pbase = &derived;
cout << "derived is: " << typeid(derived).name() << endl;
cout << " *pbase is: " << typeid(*pbase).name() << endl;
cout << boolalpha << "same type? ";
cout << ( typeid(derived)==typeid(*pbase) ) << endl << endl;
Poly_Derived polyderived;
Poly_Base* ppolybase = &polyderived;
cout << "polyderived is: " << typeid(polyderived).name() << endl;
cout << " *ppolybase is: " << typeid(*ppolybase).name() << endl;
cout << boolalpha << "same type? ";
cout << ( typeid(polyderived)==typeid(*ppolybase) ) << endl << endl;
return 0 ;
}
我的 gcc 返回的结果如下:
int is: i i is: i pi is: Pi *pi is: i derived is: 7Derived *pbase is: 4Base same type? false polyderived is: 12Poly_Derived *ppolybase is: 12Poly_Derived same type? true
感觉这里返回的结果和 mangling 后的函数类似…
——————-
Things look phantastic in this dimness of the dusk—the spires whose bases are lost in the dark and tree tops like blots of ink. I shall wait for the morning and wake up to see thy city in the light.
[...] 有了这样一个大概的结构,我们就可以试图定义类似 any_cast 这种操作了,判断 letter 是否为 0,是的则 dynamic_cast 并通过 get 获得需要的信息,否则取 letter 对应的进行 dynamic_cast。当然 boost 的实现(一些早期的研究见这里)跟这个还是有一定的差异的,但是意思上大同小异。 [...]
C++ 的 idioms(二) « demonstrate 的 blog
2012/02/19 at 5:35 AM