Linux下的dlopen、dlsym、dlclose 相当于windows平台的LoadLibrary、GetProcAddress 、FreeLibrary,可以在运行时动态加载动态库,使用其中的导出函数。但是局限在于,这样仅仅能够导出全局函数,而不能导出类的方法。所以一般动态库导出C++类实现的功能时都会设计一大堆的全局函数来包装一下。比如(伪代码)
class A{ public: int run(...){...}; }
导出函数及使用可能会是:
//导出函数 handle_type AOpen(parameters...); int ARun(handle_type handle, parameters...); void AClose(handle_type handle); //使用时可能是: handle_type AHandle = -1; AHandle = AOpen(....); //获取句柄,即在AOpen中实例化一个A对象 ARun(AHandle, ....); //句柄亦做为参数,即通过句柄在内部找到由AOpen生成的实例 AClose(AHandle); //通过句柄释放先前生成的对象 //内部原理:动态库内部维护一张表(句柄与A类一一对应), // 每次AOpen都生成新A对象和新AHandle,放入 // 内部表, 以后调用此A对象的方法时,都需要通过 // 句柄先在内部映射表中找到对象。
这多少有些郁闷吧,要是能直接像动态连接一样使用类多好。比如一拿到A.so直接new A()就能用。可是这是不行的,dlopen仅能导入函数,并且是c风格的名字类型。
C++ dlopen mini HOWTO里给了一种折中方案,就是通过导出“创建类的函createA数”和“释放类的函数releaseA”来使用动态库中的类,这和模式设计中的工厂模式是一样的。
具体的做法是:
1. 准备一份头文件,加入一个纯虚父类ABase,并声明createA,releaseA;
2. 在生成A.so的实现源码中,写业务类AHello,继承ABase,并导出上面提到的两个函数createA,releaseA;
3. 需要使用A.so的代码,使用dlopen打开so文件,dlsym导入createA,releaseA, 调用createA返回ABase指针类型的实际是AHello类型的实例,便可以使用这个AHello类实例了;
4. 调用raleaseA释放AHello实例。
注意:
1. 基类是必须是,不然编译器怎么在编译的时候知道AHello的实现在哪里?
2. 函数导入出需要加上extern “C”防止导出名字被修改。
如此一来,使用某个导出类的动态库*.so,只要拿到它的父类声明就可以dlopen动态导入了,可以实现下面形式的调用。
int main() { void *dl = load_so("./a.so"); test_so(dl); dl = load_so("./b.so"); test_so(dl); dl = load_so("./c.so"); test_so(dl); return 0; }
示例代码:exportclass
参考:
1. http://www.faqs.org/docs/Linux-mini/C++-dlopen.html
2. http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx (未细读,可能不相关)