关于Interface和Callback的一些杂记

Java中有一个名为interface的关键字用于定义接口类 ,"an interface is a group of related methods with empty bodies"

C++中没有这个关键字,但是对于只含有纯虚函数的C++类来说,它和Java中的Interface也差不多。

interface常被用来实现callback。比如我们正在写一个http服务器,它每收到一个http请求就去执行一些特定的代码。为了把基础框架和应用层代码分开,callback就是很好的模式。

C语言中既没有类也没有虚函数,所以在C的函数库中,callback通常用一个函数指针加一个void*来实现。那个void*可以被用来传递任意句柄。通常来说,我把那个void*就看成是this指针。

C++中实现functor的三种方式:

  1. 函数指针 + void*
  2. struct/class重载operator()
  3. lambda表达式

这三种都可以统一被转换成std::function。

callback有时会让C/C++的内存管理变得很复杂。如果能做到谁分配谁释放,那么内存管理就再简单不过了。但是常常并非如此。

假设我们有一个事件(event)管理器,它有一个事件循环,每当收到一个新事件,就去调用这个事件对应的callback。

但是我们的程序又是多线程的,所以这个事件管理器本身必须被锁保护。那么就存在一个问题,callback执行的时候,应处于临界区内还是外?即,此时该线程是否持有事件管理器内的锁? 答案通常是"不",为了避免死锁。就算没有死锁,在这种情况下是否依然能触发并处理新事件?非常复杂。

如果callback执行的时候并不拥有事件管理器的任何锁,但是它又要访问事件管理器,那么就存在一个问题:如何维护事件管理器的生命周期?如果我在本次事件处理中调用了事件管理器的shutdown方法呢?这时就得靠引用计数来解决问题。事件管理器内部得有一个引用计数器。初始值为1。每调用一次callback之前加1,完成之后减1。

this->ref++;
this->unlock();
do_callbacks(.....);
this->lock();
this->ref--;
if(!this->ref) delete this;

API: base class -> interface + context

API中如果要求用户传递来的对象必须从某个类继承,那么这个类应该是interface(或者C++中的纯虚类)

Bad example: 比如我之前在完美的时候,我们做了一套事务性数据库。写应用的人要去写存储过程,而每个存储过程应当从一个叫做xdb.Procedure的类继承。基类提供了callback和commit方法供子类调用。

但是实际上我发现,大部分开源项目中,更通用的做法是把这些函数封装到一个叫做context的对象中,以参数的方式传递给需要被实现的函数。这样就不需要从一个普通类继承了,而变成实现了一个接口。

接口,就是一种调用约定。它把一串执行过程切成一片片的。是最重要的解耦工具。比如在看C函数库的API时,每当看到函数指针+void*,就应该立即反应过来:它要的是一个callback!

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥