在项目中,如果你想方便的扩展现有的应用程序的功能linux编码,你会如何实现呢?想到的思路就是使用插件系统,插件系统只是在指定文件夹中搜索 .so,如果找到,则将内容添加到程序中。当然,因为程序实际上并不知道 .so动态库中的内容,所以通常的方法是让 .so 定义一个集合入口点并调用程序本身定义的函数,然后程序可以使用这些动态库提供的接口功能。
什么是插件?
插件是共享库(dll 或 so,取决于您的平台)内的组件。这些组件在运行时按需动态加载。插件系统注册表存储有关可以加载的不同组件的信息。应用程序可以查询插件注册表,找到它需要加载的组件,然后在运行时动态加载它。
加载库
程序运行时会自动搜索该目录,并动态加载目录中的插件。只需调用 file.find(".so")
void?findAllPlugins(const?std::string&?path,?std::list<std::string>&?pluginNames)?{ ?DIR?*pDIR; ?struct?dirent?*entry; ?if(?pDIR=opendir(path.c_str())?){ ??while(entry?=?readdir(pDIR)){ ???std::string?file(entry->d_name); ???if(?file.compare(".")?>?0?&&?file.compare("..")?>?0)?{ ????std::cout?<<?file?<<?std::endl; ????if(file.find(".so")?!=?std::string::npos?)?{ ?????pluginNames.push_back(std::move(file)); ????} ???} ??} ??closedir(pDIR); ?}?else?{ ??std::cout<<?"Can?not?open?dir:?"<std::endl; ?} }
功能是加载plugins目录中的所有so插件,打印出so相关信息
应用程序向插件提供接口
为了实现功能扩展,应用程序必须向插件提供接口。在baseClass.hpp中定义一个抽象类baseTest作为接口:
#ifndef?BASECLASS_HPP_ #define?BASECLASS_HPP_
#include?
class?baseTest?{ public: ?virtual?int?PrintThis(const?std::string&?message)=0; ?virtual?int?sum(int?a,?int?b)=0; ?virtual?~baseTest(){} };
typedef?baseTest*?createTest_t(); typedef?void?deleteTest_T(baseTest?*ptr);
#endif?/*?BASECLASS_HPP_?*/
插件实现
在test.h中定义Test ,让Test 继承并实现baseTest中提供的所有接口:
#ifndef?TEST_H #define?TEST_H
#include?"../project/baseClass.hpp"
class?Test?:?public?baseTest?{ public: ????Test(); ????virtual?~Test(); ????virtual?int?PrintThis(const?std::string&?message); ????virtual?int?sum(int?a,?int?b); };
#endif?//?TEST_H
test.cpp
#include?"test.h" #include?
Test::Test()?{ ????std::cout<<"Create?new?object?from?library?success\n"; }
Test::~Test()?{ ?std::cout<<"Destroy?object?from?library?success\n"; }
int?Test::PrintThis(const?std::string?&message)?{ ????std::cout<<"Write?new?message?"?<<?message<<std::endl; ????return?static_cast<int>(message.size()); }
int?Test::sum(int?a,?int?b)?{ ?return?a+b; }
extern?"C"?baseTest*?createTest()?{ ????return?new?Test(); }
extern?"C"?void?deleteTest(baseTest*?ptr)?{ ????return?delete?ptr; }
在linux系统下面,动态链接库的生成比较简单,如果使用g++进行编译的话,只需要加上 -fPIC 和 -shared 两个选项即可。为了让应用程序动态加载插件,需要将插件编译为so文件。
g++?-shared?-o?"libplugin.so"??./test.o
至此,一个libplugin.so插件就实现了。
实现应用程序
将所有插件编译为so文件并放入当前工程目录下的plugin/Debug目录中,启动应用程序,插件自动被加载到程序中.
应用程序编写自己的插件子系统的 C++ 简单示例如下:
int?main(int?argc,?char?*argv[])?{ ?const?std::string?pluginsPath("/plugin/Debug"); ?std::list<std::string>?pluginNames; ?std::list?handlers; ?findAllPlugins(pluginsPath,pluginNames);
?for(auto?name?:?pluginNames)?{ ??std::cout<<"Find?plugin?"<std::endl; ??void*?plugin?=?dlopen((pluginsPath?+?"/"?+??name).c_str(),?RTLD_NOW); ?????if(!plugin)?{ ?????????std::cout<<"Can?not?load?library"<std::endl; ?????????continue; ?????} ?????dlerror(); ?????createTest_t*?creator?=?reinterpret_cast(dlsym(plugin,"createTest")); ?????if(!creator)?{ ?????????std::cout<<"Could?not?create?Test?"<std::endl; ?????????dlclose(plugin); ?????????continue; ?????} ?????dlerror(); ?????deleteTest_T*?del?=?reinterpret_cast(dlsym(plugin,"deleteTest")); ?????if(!del)?{ ?????????std::cout<<"Could?not?delete?Test?"<std::endl; ?????????dlclose(plugin); ?????????continue; ?????} ?????task?t; ?????t.plugin?=?plugin; ?????t.del?=?del; ?????t.test?=?creator(); ?????handlers.push_back(std::move(t)); ?} ?int?i?=?0; ?for(auto?h?:?handlers)?{ ??h.test->PrintThis("Hello?"); ??++i; ??std::cout<sum(i,i+1)<<std::endl; ??h.del(h.test); ??dlclose(h.plugin); ?} ????return?0; }
编译运行:

总结
插件是共享库内的组件,这些组件在运行时按需动态加载。程序主要实现步骤为应用程序提供接口,由用户或第三方实现接口,在编译出相应的动态链接库so(即插件)。我们在放把插件放在指定目录下(plugin),然后应用程序启动时候遍历目录找到插件,实现动态加载插件。
(编辑:通辽站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|