关于type erasure

时间:2023-03-09 19:50:07
关于type erasure

哇,好久没有写blog了,再不写的话,blog的秘密都要忘记了,嘿嘿。

最近在试着参与一个开源项目,名字叫avim(A Vibrate IM),别想多了哟。地址是:https://github.com/avplayer/avim.git  我在看它的代码的是有一个地方一直不太明白,大概就是这个页面上面的代码 https://github.com/avplayer/avim/blob/master/libavproto/include/avif.hpp,关于这个avif 类的定义和 avtcpif 之间的关系上面,我一直很模糊,后来问了代码的主人,他给我了一个说法,说它是一个 “type erasure” 的用法。之间对它还未有所闻,于是今天我关注了下它的巧妙用法。

那么什么叫type erasure呢?WiKi上面对它的定义如下

In programming languages, type erasure refers to the compile-time process by which explicit type annotations are removed from a program, before it is executed at run-time. Operational semantics that do not require programs to be accompanied by types are called type-erasure semantics, to be contrasted with type-passing semantics. The possibility of giving type-erasure semantics is a kind of abstraction principle, ensuring that the run-time execution of a program does not depend on type information. The opposite of type erasure is called reification.

大致的意思就是说,type erasure 其实是一种抽象,它忽略了具体的类型,不必对具体类型进行检查。这个功能对于强类型语言来说,比如C++, 就是一种解放。因为在C++中我们会由于类型不匹配而遇到大堆的编译错误,当然这并不是type erasure主要解决的问题。

在说到type erasure的定义的时候,一些人会想到模板,对,模板也算是一种类型弱化吧,假如,我们需要一个功能,且它需要是弱类型的(即忽略类型),那么我们会想到什么呢? 模板,虚函数,yeah.

比如要实现一个function,那么模板的写法会是:

template <typename T>
void function(T arg1)
{
  return arg1.function();
}

虚函数的做法:

class base
{
virtual void function() = 0;
}
class derived : public base
{
virtual void function()
{
......
}
};
void function(base* ptr)
{
return ptr->function();
}

这两种方法,都实现了弱类型的特点,但它们有各自的缺点,比如模板的方法,你要是有100个类型的话,系统会根据相应类型在编译期间生成100个不同版本的function,浪费了大量的编译时间;虚函数的方法,也不那么招人喜欢,因为如果想要使用function这个接口,你的类型必须继承自class base; 这是当年MFC的传统做法,如果你想使用某个功能,你要继承某个类,有一种被强奸的感觉。老一代的程序员们都已经习惯了这种做法,并且很多人还沉浸在继承带来的优越感之中,但他们也被继承引来了不少的不便,这里就不细谈了。

这个时候用过boost的同学,他们可能会想到boost里面的boost::any, 或者boost::function, 当然 boost::function这个东西已经加入到C++标准里面了。std::function可以存储什么呢?

它可以存储函数指针,lambda表达式,函数对象 等。对于强类型语言C++来说,这是很不可思议的事情。而boost::any呢,它可以存储任意的类型,卧槽,神马?任意类型,对,你没看错,是任意类型。那么你想到什么没有,之前我有一个需求,需要做一个可接受任意参数的接口,我第一时间想到了C语言里类似跟printf实现原理一样东西,可我嫌那样太不美观,我就使用了boost::any,加一个vector来存储变量,这样很简单的实现了接受任意参数的接口。那么你有没有想过boost::any和boost::function,为什么能够做到这样的弱类型?其实很简单,具体的话你参考下boost::any的源码,当然也可以参考我自己写的一份简单的原理性代码,

地址:https://github.com/xiaopeifeng/CodeTricks/blob/master/type_erasure.cc

如果硬要写一份模板代码的话,那么我想应该是这个样子的:

class Base {
public:
virtual ~Base();
virtual some_common_func() = 0 ;
} ; template < class T >
class adapter : public Base
{
T t;
public:
some_common_func()
{
t.some_common_func();
}
adapter(T _t): t(_t){}
}; class interface
{
Base * ptr;
public:
template<class T>
interface(T t)
{
ptr = new adapter<T>(t);
}
some_common_func()
{
ptr->some_common_func();
}
};

这个class interface才是你需要的具体的类型,你就完全可以在后面的代码里使用interface去作为接口参数,而具体的实现则可以自己再定义另外的类,而这个类不必去继承自interface(但是它却有一个is a 的语义), 只需要去实现some_common_func的功能就行了,这样子就对接受interface为参数的接口,就有了type erasure的特性了,因为它不会再去检查具体的参数类型了,只要改参数实现了需要调用的方法就OK了。

参考:https://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/