Plugin 的创建及使用
- 使用
BOOST_DLL_ALIAS
定义插件接口。 - 使用
import_alias
导入插件接口。 - 使用
creator
/Factory
模式,提供创建插件实例的接口。
Demo Code: test_plugin_dll
1. 实现插件接口
1.1 DSO/DLL原型定义
1
2
3
4
5
6
7
8
9
10
11
12
13
class DIInterface {
public:
DIInterface() = default;
virtual ~DIInterface() = default;
//virtual std::shared_ptr<DIInterface> clone() = 0;
virtual void loadInfo(const std::string& dataFile) = 0;
virtual QWidget* getWidget() = 0;
virtual void unload() = 0;
protected:
QWidget* widget_;
};
1.2 接口定义及实现
1
2
3
4
5
6
7
8
std::shared_ptr<test::plugin::DIInterface> diLoader(const std::string& infoFile) {
std::shared_ptr<test::plugin::DIInfoChaoke> ptr = std::make_shared<test::plugin::DIInfoChaoke>();
ptr->loadInfo(infoFile);
return ptr;
}
BOOST_DLL_ALIAS(diLoader, // 被封装成插件接口的函数名
diLoaderAlias); // 别名,可用于创建插件实例
2. 插件加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// static const char* kDILoaderFnName = "diLoaderAlias";
const std::string dir_path = boost::dll::program_location().parent_path().string();
const auto chaoke_dll_path = fs::path(dir_path) / "di_plugin_chaoke.dll";
lib_ = boost::dll::shared_library(chaoke_dll_path.string());
try {
auto creator = boost::dll::import_alias<di_loader_proto_t>(chaoke_dll_path.string(), kDILoaderFnName,
boost::dll::load_mode::append_decorations);
auto loader_proto = lib_.get_alias<di_loader_proto_t>(kDILoaderFnName);
instance_ = loader_proto(dataFilePath);
} catch (const std::exception& e) {
SPDLOG_WARN("Failed to load dll: {}", e.what());
return false;
}
3. 插件卸载
在卸载DSO/DLL
之后,由于类的成员函数代码内存被释放,故其vtable
所指向的内存(即成员方法)变成非法地址,在调用其成员方法函数,以及析构函数时,会导致程序崩溃。
所以在卸载DLL/DSO
之前,需要先释放外部持有的资源:
- 调用
unload
接口,释放资源,比如释放QWidget
; - 释放外部持有的
shared_ptr
;
4. 技巧:将boost::dll::shared_library
生命周期与Plugin实例对象生命周期绑定
定义如下deletor
:
1
2
3
4
5
6
7
struct library_holding_deleter {
library_holding_deleter(std::shared_ptr<boost::dll::shared_library> libDLL) : lib_(libDLL) {}
void operator()(DIInterface* p) const { delete p; }
std::shared_ptr<boost::dll::shared_library> lib_;
};
在创建DIInterface
实例时,传入library_holding_deleter
(make_shared不支持传入自定义deleter
):
1
2
3
4
5
6
7
8
// auto lib_dll = std::make_shared<boost::dll::shared_library>(chaoke_dll_path.string());
// library_holding_deleter deletor(lib_dll);
std::shared_ptr<test::plugin::DIInterface> diLoader(const std::string& infoFile, library_holding_deleter deletor) {
std::shared_ptr<test::plugin::DIInfoChaoke> ptr(new test::plugin::DIInfoChaoke, deletor);
ptr->loadInfo(infoFile);
return ptr;
}
5. 资料
本文由作者按照 CC BY 4.0 进行授权