demonstrate 的 blog

daily blog

any 的实现

with one comment

我们简单的研究一下 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.

Written by zt

2010/12/11 at 4:29 PM

Posted in c/c++

Tagged with , ,

One Response

Subscribe to comments with RSS.

  1. [...] 有了这样一个大概的结构,我们就可以试图定义类似 any_cast 这种操作了,判断 letter 是否为 0,是的则 dynamic_cast 并通过 get 获得需要的信息,否则取 letter 对应的进行 dynamic_cast。当然 boost 的实现(一些早期的研究见这里)跟这个还是有一定的差异的,但是意思上大同小异。 [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.