From d42e8487bbd5174750519bf7ff92a90e172b8321 Mon Sep 17 00:00:00 2001 From: hxf0223 Date: Tue, 15 Oct 2024 10:22:12 +0800 Subject: [PATCH] add _posts/2024-10-15-Plugin-Boost-DLL.md --- _posts/2024-10-15-Plugin-Boost-DLL.md | 109 ++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 _posts/2024-10-15-Plugin-Boost-DLL.md diff --git a/_posts/2024-10-15-Plugin-Boost-DLL.md b/_posts/2024-10-15-Plugin-Boost-DLL.md new file mode 100644 index 0000000..7373b6e --- /dev/null +++ b/_posts/2024-10-15-Plugin-Boost-DLL.md @@ -0,0 +1,109 @@ +--- +title: 使用 Boost::DLL 创建及使用插件 +date: 2024-10-15 +0800 # 2022-01-01 13:14:15 +0800 只写日期也行;不写秒也行;这样也行 2022-03-09T00:55:42+08:00 +categories: [c++] +tags: [c++, plugin] # TAG names should always be lowercase + +# 以下默认false +math: true +mermaid: true +# pin: true +--- + + +1. 使用 `BOOST_DLL_ALIAS` 定义插件接口。 +2. 使用 `import_alias` 导入插件接口。 +3. 使用 `creator` / `Factory` 模式,提供创建插件实例的接口。 + +## 1. 实现插件接口 ## + +### 1.1 DSO/DLL原型定义 ### + +```c++ +class DIInterface { + public: + DIInterface() = default; + virtual ~DIInterface() = default; + //virtual std::shared_ptr clone() = 0; + + virtual void loadInfo(const std::string& dataFile) = 0; + virtual QWidget* getWidget() = 0; + virtual void unload() = 0; + + protected: + QWidget* widget_; +}; +``` + +### 1.2 接口定义及实现 ### + +```c++ +std::shared_ptr diLoader(const std::string& infoFile) { + std::shared_ptr ptr = std::make_shared(); + ptr->loadInfo(infoFile); + return ptr; +} + +BOOST_DLL_ALIAS(diLoader, // 被封装成插件接口的函数名 + diLoaderAlias); // 别名,可用于创建插件实例 +``` + +## 2. 插件加载 ## + +```c++ +// 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(chaoke_dll_path.string(), kDILoaderFnName, + boost::dll::load_mode::append_decorations); + auto loader_proto = lib_.get_alias(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`之前,需要先释放外部持有的资源: + +1. 调用`unload`接口,释放资源,比如释放`QWidget`; +2. 释放外部持有的`shared_ptr`; + +## 4. 技巧:将`boost::dll::shared_library`生命周期与Plugin实例对象生命周期绑定 ## + +定义如下`deletor`: + +```c++ +struct library_holding_deleter { + library_holding_deleter(std::shared_ptr libDLL) : lib_(libDLL) {} + + void operator()(DIInterface* p) const { delete p; } + + std::shared_ptr lib_; +}; +``` + +在创建`DIInterface`实例时,传入`library_holding_deleter`(make_shared不支持传入自定义`deleter`): + +```c++ +// auto lib_dll = std::make_shared(chaoke_dll_path.string()); +// library_holding_deleter deletor(lib_dll); + +std::shared_ptr diLoader(const std::string& infoFile, library_holding_deleter deletor) { + std::shared_ptr ptr(new test::plugin::DIInfoChaoke, deletor); + ptr->loadInfo(infoFile); + return ptr; +} +``` + +## 5. 资料 ## + +* [Boost DLL -- Plugin](https://www.boost.org/doc/libs/1_86_0/doc/html/boost_dll/tutorial.html) +* [跨平台的 plugin 開發函式庫:Boost DLL - 進階](https://viml.nchc.org.tw/archive_blog_614/)