我们在编写c 框架时,经常会涉及到一项基础技术,就是根据“一个动态库 一个类名称字符串“,动态的创建类对象。
这样做的好处是可以实现框架与业务代码的彻底解耦。框架不用关心业务侧的具体实现细节,只需要提供一个基类由业务方继承实现,然后业务方在配置文件中配置对应的动态库 类名称即可实现自动加载并运行。我们通常把这类功能称为classloader,今天就带大家一起来实现下。
一、实现原理
classloader一般包含shared_library动态库加载/卸载、object_factory动态对象创建工厂、class_register动态类注册,以及class_loader统一管理部分。整体关系如下图:
动态对象创建的关键是,框架不知道业务类的任何信息,所以需要业务侧在实现derived类(即图中democomponent)之后,使用register_class宏注册类以让框架知晓(注册时,宏需要自动为类生成一个creater方法并注册给objectfactory,因此保存注册信息的objectfactory需要使用单例)。
框架持有每个业务组件类的creater方法之后,在需要创建createobject时使用单例保存的业务类注册creater即可创建对应对象。
二、具体实现
1、shared_library动态库加载/卸载
这部分是最简单的,我们使用dlopen、dlclose来实现即可,简单的封装下,这里不赘述,我们重点讲解后续部分。
class sharedlibrary {
...
bool load(const std::string &libraryfile){
...
// load so
_libraryhandle = dlopen(libraryfile.c_str(), real_flag);
if (!_libraryhandle) {
const char *err = dlerror();
log(error) << "library [" << libraryfile << "] load fail: " << std::string(err);
return false;
}
...
}
...
bool unload(){
...
// unload so
if (_libraryhandle) {
log(info) << "unload";
dlclose(_libraryhandle);
_libraryhandle = nullptr;
}
...
}
...
}
2、object_factory对象创建工厂与class_register动态类注册
object_factory对象工厂是classloader的核心,决定了如何完全松耦合的实现动态类创建。class_register作为辅助,为object_factory的预先注册提供宏支持。
2.1 新手入门
提到动态创建对象,相信新手想到的最简单的方法通常如下:
void createobject(std::string objname)
{
if(objname == “a”)
{
new a;
}
else if(objname == “b”)
{
new b;
}
...
}
但是这里有个问题,框架编写时并不知道业务要起什么类名,框架编译时也没有这些类的so库,又如何在代码中提前编写new 类名的代码呢?况且业务代码一直在变,框架怎么可能跟着业务变化一直修改这些地方呢?显然这种方法是不可行的。
2.2 宏注册
为了解决这个问题,接下来很多同学会想到可以用宏来实现传入字符串,new出对应的类,这个思路很好,所以我们接下来实现了一个宏定义:
// 动态注册类creator
#define register_class(classname) \
classname *create##classname() \
{ \
return new classname; \
} \
static bool g_reg_##classname = objectfactory::getinstance()->register(#derived, create##uniqueid);
上面这个宏很简单,首先是定义了一个new对象的函数,new一个传入类名的对象。接着定义了一个static g_reg_xx变量,这个变量被初始化的时候,会调用到objectfactory::register来保存类名和类creater方法的关系。这里我们使用static getinstance单例函数,确保在所有static g_reg_xx对象构造之前,objectfactory单例对象已经完成了初始化。这里的static g_reg_xx变量也可以使用struct构造函数来代替,目的都是为了在加载register_class代码之后就能自动创建一个static对象,从而自动完成类注册动作。
接下来,我们要实现下register逻辑,这里比较简单,实现一个objectfactory单例,把register传入的类名和类creater方法保存下来备用即可:
class objectfactory{
// 动态注册类
using objcreator = std::function