Files
ViewDesignEngine/include/vde/plugin/plugin_system.h
T
茂之钳 5e2812a6f3
CI / Build & Test (push) Failing after 33s
CI / Release Build (push) Failing after 35s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v6.2): Blender addon + .NET/C# bindings + developer docs + plugin system
v6.2.1 — Blender Integration Addon:
- 5 files, 1,732 lines: __init__, operators (9 ops), panels (4 panels)
- preferences, vde_bridge (subprocess CLI bridge)
- Import/Export STEP, primitives, boolean, SDF→Mesh

v6.2.2 — .NET/C# Bindings (VdeSharp):
- 8 files: NativeMethods (50+ P/Invoke), BrepModel, MeshData, SdfEngine, Assembly
- C API extended with 20 new functions (STEP I/O, Boolean, SDF, Assembly)
- vde_capi rebuilt as SHARED library
- Example: import STEP → boolean → export

v6.2.3 — Developer Docs + Plugin System:
- 7 docs: architecture, contributing, API overview, building, testing, plugin-system
- plugin_system.h/.cpp: PluginInterface, PluginManager, dlopen/LoadLibrary
- README.md updated with v6 features
- Syntax-check passed (GCC 10.2.1, -Wall -Wextra -Wpedantic)
2026-07-26 22:24:40 +08:00

397 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
// ═══════════════════════════════════════════════════════════
// 平台宏 — 跨平台动态库导入/导出
// ═══════════════════════════════════════════════════════════
#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 <email>" 或 "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<std::string> 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<PluginManifest> from_file(const std::string& path);
/// 从 JSON 字符串解析清单
/// @param json JSON 文本内容
/// @return 清单对象,解析失败返回 nullopt
static std::optional<PluginManifest> 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<std::string, std::string>& 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<PluginInterface> 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<std::string> 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> 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<MyExporter*>(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