#pragma once /** * @file plugin_system.h * @brief 插件系统 — 动态加载、生命周期管理、元数据定义 * * ## 设计目标 * - 零侵入:插件独立编译,不修改核心代码 * - 动态加载:运行时 dlopen/LoadLibrary 加载 * - 版本兼容:Manifest 声明版本约束,自动检查 * - 隔离性:插件崩溃不影响主程序 * * ## 核心组件 * - PluginInterface: 所有插件必须实现的抽象基类 * - PluginManifest: 插件元数据(name/version/author/type) * - PluginManager: 加载/卸载/查询插件的管理器 * * ## 使用示例 * @code * vde::plugin::PluginManager mgr; * mgr.load_plugin_dir("/usr/lib/vde/plugins"); * auto* p = mgr.get_plugin("io.export.gltf"); * p->execute("export", params, &mesh, &result); * mgr.unload_all(); * @endcode * * @ingroup plugin * @since v6.0 */ #include #include #include #include #include #include // ═══════════════════════════════════════════════════════════ // 平台宏 — 跨平台动态库导入/导出 // ═══════════════════════════════════════════════════════════ #if defined(_WIN32) || defined(_WIN64) #ifdef VDE_PLUGIN_BUILD #define VDE_PLUGIN_EXPORT __declspec(dllexport) #else #define VDE_PLUGIN_EXPORT __declspec(dllimport) #endif #define VDE_PLUGIN_LOCAL #else #if __GNUC__ >= 4 #define VDE_PLUGIN_EXPORT __attribute__((visibility("default"))) #define VDE_PLUGIN_LOCAL __attribute__((visibility("hidden"))) #else #define VDE_PLUGIN_EXPORT #define VDE_PLUGIN_LOCAL #endif #endif namespace vde::plugin { // ═══════════════════════════════════════════════════════════ // PluginManifest — 插件元数据 // ═══════════════════════════════════════════════════════════ /// 插件元数据,提供插件的身份信息和兼容性约束。 /// /// 每个插件在 initialize() 中填充此结构。PluginManager /// 在加载时读取 manifest 检查版本兼容性。 /// /// @see plugin.json 清单文件格式 struct PluginManifest { /// 唯一标识符,遵循反向域名格式 /// 示例: "io.export.usdz", "geometry.filter.laplacian" std::string name; /// 语义化版本号 "major.minor.patch" /// 示例: "1.0.0", "2.1.3-beta" std::string version; /// 作者信息,格式 "Name " 或 "Organization" std::string author; /// 插件功能描述(单行简介) std::string description; /// 许可证标识符 (SPDX) /// 示例: "MIT", "Apache-2.0", "GPL-3.0-only" std::string license; /// 最低兼容的 VDE 版本 "6.0.0" std::string vde_version_min; /// 最高兼容的 VDE 版本(空字符串 = 无上限) std::string vde_version_max; // ══════════════════════════════════════════════ // 插件类型枚举 // ══════════════════════════════════════════════ enum class Type : uint8_t { IO_IMPORT = 0, ///< 格式导入器 (STEP额外格式, USDZ, FBX...) IO_EXPORT = 1, ///< 格式导出器 (USDZ, FBX, X3D...) GEOMETRY_FILTER = 2, ///< 几何过滤器 (平滑, 简化, 特征提取) CUSTOM_TOOL = 3, ///< 自定义工具 (应力分析, 拓扑优化...) OTHER = 255 ///< 未分类 }; Type type = Type::OTHER; /// 依赖的其他插件名称列表 /// 加载此插件前必须先加载依赖项 std::vector dependencies; /// 动态库文件名(用于清单文件加载模式) /// 示例: "libvde_io_usdz.so" std::string library; /// 入口点符号名称 struct EntryPoints { std::string create = "create_plugin"; ///< 工厂函数符号 std::string destroy = "destroy_plugin"; ///< 销毁函数符号 std::string get_manifest = "get_manifest"; ///< 清单查询(可选) }; EntryPoints entry_points; // ── 工厂方法 ────────────────────────────────── /// 从 plugin.json 文件加载清单 /// @param path JSON 文件路径 /// @return 清单对象,解析失败返回 nullopt static std::optional from_file(const std::string& path); /// 从 JSON 字符串解析清单 /// @param json JSON 文本内容 /// @return 清单对象,解析失败返回 nullopt static std::optional from_json(const std::string& json); /// 获取类型对应的可读字符串 static const char* type_name(Type t); /// 验证清单必要字段是否完整 /// @return true 如果 name/version/vde_version_min 均已设置 bool valid() const; }; // ═══════════════════════════════════════════════════════════ // PluginInterface — 插件抽象基类 // ═══════════════════════════════════════════════════════════ /// 所有 VDE 插件必须实现的抽象基类。 /// /// 最小实现要求: /// - initialize() — 设置 manifest 并初始化资源 /// - shutdown() — 清理所有资源 /// - manifest() — 返回 const 引用 /// - name() — 返回 manifest().name.c_str() /// - version() — 返回 manifest().version.c_str() /// - execute() — 实现插件核心功能 /// /// ## 生命周期 /// ``` /// create_plugin() → initialize() → execute(...)* → shutdown() → destroy_plugin() /// ``` class PluginInterface { public: PluginInterface() = default; virtual ~PluginInterface() = default; // 禁止拷贝,允许移动 PluginInterface(const PluginInterface&) = delete; PluginInterface& operator=(const PluginInterface&) = delete; PluginInterface(PluginInterface&&) noexcept = default; PluginInterface& operator=(PluginInterface&&) noexcept = default; /// 初始化插件。在 dlopen + create_plugin 之后调用一次。 /// 应在此设置 manifest 并分配资源。 /// @return true 表示初始化成功 virtual bool initialize() = 0; /// 关闭插件。在 dlclose 之前调用一次。 /// 应在此释放所有资源。 virtual void shutdown() = 0; /// 获取插件清单的只读引用 virtual const PluginManifest& manifest() const = 0; /// 插件唯一名称(与 manifest().name 一致) virtual const char* name() const = 0; /// 插件版本(与 manifest().version 一致) virtual const char* version() const = 0; /// 执行插件核心功能 /// /// @param action 操作名称,如 "export", "filter", "analyze" /// @param params 参数键值对,如 {"path":"/tmp/out.glb", "binary":"true"} /// @param input 输入数据指针(类型由 action 定义) /// @param output 输出数据指针(类型由 action 定义,调用方负责生命周期) /// @return true 表示执行成功 virtual bool execute(const std::string& action, const std::map& params, void* input, void* output) = 0; /// 查询插件是否处于就绪状态 /// @return true 表示 initialize() 已完成且未 shutdown virtual bool ready() const { return true; } /// 返回最近一次错误描述 virtual std::string error() const { return {}; } }; // ═══════════════════════════════════════════════════════════ // PluginManager — 插件加载/卸载/生命周期管理 // ═══════════════════════════════════════════════════════════ /// 插件管理器:扫描目录、加载动态库、维护注册表。 /// /// 线程安全:所有公开方法持有内部读写锁。 /// /// ## 搜索路径优先级 /// 1. load_plugin_dir() 指定的目录 /// 2. 环境变量 $VDE_PLUGIN_PATH(冒号分隔) /// 3. 默认路径: /usr/lib/vde/plugins, ./vde_plugins /// /// @code /// PluginManager mgr; /// mgr.load_plugin_dir("/usr/lib/vde/plugins"); /// mgr.load_plugin("/opt/custom/my_plugin.so"); /// /// if (auto* p = mgr.get_plugin("io.export.gltf")) { /// p->execute("export", {}, &mesh, &buf); /// } /// @endcode class PluginManager { public: PluginManager(); ~PluginManager(); // 禁止拷贝 PluginManager(const PluginManager&) = delete; PluginManager& operator=(const PluginManager&) = delete; // ── 目录/批量加载 ─────────────────────────────── /// 扫描并加载指定目录下的所有插件 /// /// 识别 .so / .dylib / .dll 文件。 /// 如果同目录存在 plugin.json 清单,优先读取以验证兼容性。 /// /// @param dir_path 插件目录路径 /// @return 成功加载的插件数量 /// @note 单个插件加载失败不影响其他插件 int load_plugin_dir(const std::string& dir_path); /// 通过环境变量和默认路径加载插件 /// 等价于 load_plugin_dir() 遍历 $VDE_PLUGIN_PATH /// @return 成功加载的插件总数 int load_default_paths(); // ── 单个加载 ───────────────────────────────────── /// 加载单个插件动态库 /// /// 流程: dlopen → dlsym(create_plugin) → initialize() /// /// @param plugin_path 动态库完整路径(.so/.dylib/.dll) /// @return true 表示加载成功 bool load_plugin(const std::string& plugin_path); /// 注册已创建的插件实例(通常由静态注册宏使用) /// /// @param plugin 插件实例(所有权转移给 PluginManager) /// @return true 表示注册成功(名称不冲突) bool register_plugin(std::unique_ptr plugin); // ── 卸载 ───────────────────────────────────────── /// 按名称卸载指定插件 /// /// @param name 插件名称(manifest().name) /// @return true 表示找到并卸载成功 bool unload_plugin(const std::string& name); /// 卸载所有已加载插件 /// 按加载倒序依次 shutdown + destroy void unload_all(); // ── 查询 ───────────────────────────────────────── /// 按名称查找已加载插件 /// /// @param name 插件名称 /// @return 插件指针,未找到返回 nullptr PluginInterface* get_plugin(const std::string& name); /// 按名称查找(const 重载) const PluginInterface* get_plugin(const std::string& name) const; /// 列出所有已加载插件的名称 std::vector list_plugins() const; /// 检查指定插件是否已加载 bool is_loaded(const std::string& name) const; /// 已加载插件数量 size_t plugin_count() const; /// 最近一次错误的描述 std::string last_error() const; /// 清空错误状态 void clear_error(); private: struct Impl; std::unique_ptr impl_; }; // ═══════════════════════════════════════════════════════════ // 插件导出符号 — 每个插件动态库必须导出 // ═══════════════════════════════════════════════════════════ /// @brief 创建插件实例(必须由每个 .so/.dll 导出) /// /// 示例实现: /// @code /// extern "C" VDE_PLUGIN_EXPORT PluginInterface* create_plugin() { /// return new MyPlugin(); /// } /// @endcode using CreatePluginFunc = PluginInterface* (*)(); /// @brief 销毁插件实例(必须由每个 .so/.dll 导出) /// /// 示例实现: /// @code /// extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(PluginInterface* p) { /// delete p; /// } /// @endcode using DestroyPluginFunc = void (*)(PluginInterface*); /// @brief 获取插件清单(可选导出,优化加载速度) /// /// 示例实现: /// @code /// extern "C" VDE_PLUGIN_EXPORT const PluginManifest* get_manifest() { /// static PluginManifest m = { .name = "...", .version = "1.0.0", ... }; /// return &m; /// } /// @endcode using GetManifestFunc = const PluginManifest* (*)(); // ═══════════════════════════════════════════════════════════ // 便捷注册宏 // ═══════════════════════════════════════════════════════════ /// 在插件 .cpp 文件中注册插件类 /// /// 自动生成 create_plugin / destroy_plugin 导出符号。 /// /// 用法: /// @code /// VDE_REGISTER_PLUGIN(MyExporter) /// @endcode /// /// 等价于手写: /// @code /// extern "C" VDE_PLUGIN_EXPORT void* create_plugin() { /// return new MyExporter(); /// } /// extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(void* p) { /// delete static_cast(p); /// } /// @endcode #define VDE_REGISTER_PLUGIN(PluginClass) \ extern "C" VDE_PLUGIN_EXPORT vde::plugin::PluginInterface* \ create_plugin() { \ return new PluginClass(); \ } \ extern "C" VDE_PLUGIN_EXPORT void \ destroy_plugin(vde::plugin::PluginInterface* p) { \ delete p; \ } /// 注册插件并同时导出 manifest 符号 /// /// @param PluginClass 插件类名 /// @param ManifestVar 静态 PluginManifest 变量名 #define VDE_REGISTER_PLUGIN_WITH_MANIFEST(PluginClass, ManifestVar) \ extern "C" VDE_PLUGIN_EXPORT const vde::plugin::PluginManifest* \ get_manifest() { \ return &ManifestVar; \ } \ VDE_REGISTER_PLUGIN(PluginClass) } // namespace vde::plugin