boost.TypeErasure

C++ 里一个重要的概念 polymorphism 分为两种:

  • runtime polymorphism 通常指的通过 virtual function 获得的同一函数不同行为(子类产生的区别)
  • compile-time polymorphism 通常有两种,函数重载(overloading 导致不同的 type 可以有不同的实现)算是一个,这里我们说的是通过 template 获得的编译时的多态,即根据 type 可以 bind 到不同的实现上,典型的例子就是模板的特化、CRTP 等。

两类多态的优缺点有:

  • 虚函数是运行时 resolve 而 template 是编译时就得确定下来,如果需要的不同效果是根据用户输入(runtime)来确定,那么一般都得通过前者
  • 虚函数的编译可以分离,即提供虚函数接口的头文件给予实现者就可以让其实现,这只包括了函数的定义;而 template 要求整个编译单元包含整个 template 的实现
  • 虚函数的函数签名是固定的,也就是输入的类必须符合说明,但是模板并没有这样的要求,不过实现依赖的一些东西构成了所谓的 concept,因此要写好 template 的文档更加困难
  • 模板每次实例化都会重新要求编译器进行优化,这能从某种程度上导致更好的二进制代码但是也会使得编译变慢、生成目标文件较大
  • 模板支持的“概念”产生了一种所为 value semantics,即便不存在继承关系的类也可以进行类似的使用(duck typing),对虚函数来说往往只能通过继承,这意味着使用引用和指针
  • 模板是开放的,可以很容易 non-intrusively 将第三方的东西和已有框架整合在一起(reusibility 和 flexibility),而虚函数需要写 wrapper 之类的实现
  • 模板函数可以表达几个类型之间的关系,而虚函数不可以

boost 原本存在一些打破 runtime/compile-time barrier 的类,如

  • boost.any 提供的指向任意类型的指针(用做异构容器)
  • boost.function 能将不同形式的函数放在运行时的对象(并且是一个类型)里面

那么 boost.TypeErasure 试图解决的就是将这个功能扩展到更一般的情形,为了实现这个它也提供了一些定义好的概念供大家使用。这么说起来是非常抽象的,我们看几个例子。

any 类型是这个 lib 的核心,它的模板参数是一个 mpl::vector 表达的概念序列,这个类型的对象打破了运行时和编译时的边界,比如我们希望一个虚函数能够接受任意“类似 int”的类型,很显然,C++ 的世界里面并没有一个什么公共的祖先类,类似 int 的概念其实是通过 copy constructible、支持某些运算等来表达的,这样我们可以利用 boost.TypeErasure 提供的概念拼一个类似 int 的 any 类型出来,我们的“虚函数”接受这样的输入,当然为了享有 compile-time type safety,我们常常将虚函数本身换成 template,而将这个函数的实现传递给使用 any 定义的实现(这一般是 protected virtual function),这样在子类里面就可以实现不同的东西了。

当然也许你在想那我要的 concept 可以很复杂,怎么可能这个 lib 能预定义好所有的概念… hmm 是否你想到了 TTI 了呢?我猜这个 lib 提供的 macro 就是建立在 TTI 之上的,TTI 仅仅提供了判定的 meta function,这个 lib 提供的 BOOST_TYPE_ERASURE_XXXX 产生的却是 concept checker,可以和预定义的 concept checker 一起用。

#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/operators.hpp>
#include <iostream>
#include <gmpxx.h>

namespace mpl = boost::mpl ;
namespace te = boost::type_erasure ;

class printer {
public:
  template <class IntType> inline void
  print (IntType i) const {
    _print (i) ;
  }

protected:
  typedef mpl::vector<te::copy_constructible<>,
                      te::typeid_<>,
                      te::multipliable<>,
                      te::ostreamable<>,
                      te::relaxed > requirements ;
  typedef te::any<requirements> int_type ;

  virtual void _print(int_type i) const = 0 ;
} ;

class quoted_printer : public printer {
protected:
  void _print(int_type i) const {
    std::cout << '"' << i*i << '"' << std::endl ;
  }
} ;

int
main (int, char**) {
  quoted_printer p ;
  p.print (5) ;

  mpz_class a ("123456789123456789") ;
  p.print (a) ;

  return 0 ;
}

以上程序简单的示意了 type erasure 的部分功能。神奇的地方在于从 template 函数传递给 protected 函数时,很显然需要做一个从 int/mpz_class 到 int_type 的转换,这个转换应该记录了类型信息,这使得后面 operator*/copy constructor/operator<< 这些操作可以 bind 到每个类自己的实现上,尽管这些方法都并不是 virtual 的,但是最后的结果看起来就是他们都像一个 virtual 函数一样被调用了。

——————
And Jacob served seven years for Rachel; and they seemed unto him but a few days, for the love he had to her.

Advertisements
boost.TypeErasure

一个有关“boost.TypeErasure”的想法

发表评论

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 / 更改 )

Twitter picture

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

Facebook photo

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

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s