502 lines
16 KiB
Markdown
502 lines
16 KiB
Markdown
|
|
# 插件系统设计
|
|||
|
|
|
|||
|
|
v6.0 引入插件系统,允许第三方开发者扩展 ViewDesignEngine 的功能,无需修改核心代码。
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
|
|||
|
|
1. [设计目标](#1-设计目标)
|
|||
|
|
2. [架构概览](#2-架构概览)
|
|||
|
|
3. [核心组件](#3-核心组件)
|
|||
|
|
4. [插件开发](#4-插件开发)
|
|||
|
|
5. [插件加载流程](#5-插件加载流程)
|
|||
|
|
6. [生命周期管理](#6-生命周期管理)
|
|||
|
|
7. [清单文件](#7-清单文件)
|
|||
|
|
8. [安全考虑](#8-安全考虑)
|
|||
|
|
9. [最佳实践](#9-最佳实践)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 设计目标
|
|||
|
|
|
|||
|
|
| 目标 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **零侵入** | 插件不修改核心代码,独立编译 |
|
|||
|
|
| **动态加载** | 运行时 dlopen 加载,无需重新编译 VDE |
|
|||
|
|
| **版本兼容** | Manifest 声明版本约束,自动检查兼容性 |
|
|||
|
|
| **隔离性** | 插件崩溃不影响主程序 |
|
|||
|
|
| **易开发** | 继承 `PluginInterface` 即可,最少样板代码 |
|
|||
|
|
|
|||
|
|
## 2. 架构概览
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Application │
|
|||
|
|
│ │ │
|
|||
|
|
│ PluginManager │
|
|||
|
|
│ ┌──────┴──────┐ │
|
|||
|
|
│ │ Registry │ │
|
|||
|
|
│ │ name→Plugin* │ │
|
|||
|
|
│ └──────┬──────┘ │
|
|||
|
|
│ ┌───────────────┼───────────────┐ │
|
|||
|
|
│ │ │ │ │
|
|||
|
|
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
|
|||
|
|
│ │Plugin A │ │Plugin B │ │Plugin C │ │
|
|||
|
|
│ │(.so) │ │(.so) │ │(.so) │ │
|
|||
|
|
│ │IO Format│ │Geometry │ │ Custom │ │
|
|||
|
|
│ │Exporter │ │Filter │ │ Tool │ │
|
|||
|
|
│ └─────────┘ └─────────┘ └─────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ Plugin Directory: /usr/lib/vde/plugins/ │
|
|||
|
|
│ ./vde_plugins/ │
|
|||
|
|
│ $VDE_PLUGIN_PATH │
|
|||
|
|
└─────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 核心组件
|
|||
|
|
|
|||
|
|
### 3.1 PluginInterface(抽象基类)
|
|||
|
|
|
|||
|
|
所有插件必须实现的接口:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
namespace vde::plugin {
|
|||
|
|
|
|||
|
|
class PluginInterface {
|
|||
|
|
public:
|
|||
|
|
virtual ~PluginInterface() = default;
|
|||
|
|
|
|||
|
|
/// 插件初始化(加载后调用一次)
|
|||
|
|
virtual bool initialize() = 0;
|
|||
|
|
|
|||
|
|
/// 插件清理(卸载前调用一次)
|
|||
|
|
virtual void shutdown() = 0;
|
|||
|
|
|
|||
|
|
/// 返回插件清单
|
|||
|
|
virtual const PluginManifest& manifest() const = 0;
|
|||
|
|
|
|||
|
|
/// 插件唯一名称(如 "io.export.usdz")
|
|||
|
|
virtual const char* name() const = 0;
|
|||
|
|
|
|||
|
|
/// 插件版本(语义化版本)
|
|||
|
|
virtual const char* version() const = 0;
|
|||
|
|
|
|||
|
|
/// 执行插件功能
|
|||
|
|
virtual bool execute(const std::string& action,
|
|||
|
|
const std::map<std::string, std::string>& params,
|
|||
|
|
void* input, void* output) = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} // namespace vde::plugin
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 PluginManifest(插件元数据)
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
namespace vde::plugin {
|
|||
|
|
|
|||
|
|
struct PluginManifest {
|
|||
|
|
std::string name; ///< 唯一标识符,如 "io.export.usdz"
|
|||
|
|
std::string version; ///< 语义化版本 "1.0.0"
|
|||
|
|
std::string author; ///< 作者信息
|
|||
|
|
std::string description; ///< 功能描述
|
|||
|
|
std::string license; ///< 许可证 "MIT" / "Apache-2.0"
|
|||
|
|
std::string vde_version_min;///< 最低 VDE 版本 "6.0.0"
|
|||
|
|
std::string vde_version_max;///< 最高兼容 VDE 版本 (空=无上限)
|
|||
|
|
|
|||
|
|
/// 插件类型
|
|||
|
|
enum class Type {
|
|||
|
|
IO_IMPORT, ///< 格式导入器
|
|||
|
|
IO_EXPORT, ///< 格式导出器
|
|||
|
|
GEOMETRY_FILTER,///< 几何过滤器
|
|||
|
|
CUSTOM_TOOL, ///< 自定义工具
|
|||
|
|
OTHER
|
|||
|
|
};
|
|||
|
|
Type type = Type::OTHER;
|
|||
|
|
|
|||
|
|
/// 依赖的其他插件名称(可选)
|
|||
|
|
std::vector<std::string> dependencies;
|
|||
|
|
|
|||
|
|
/// 从 JSON 文件加载
|
|||
|
|
static std::optional<PluginManifest> from_file(const std::string& path);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} // namespace vde::plugin
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 PluginManager(插件管理器)
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
namespace vde::plugin {
|
|||
|
|
|
|||
|
|
class PluginManager {
|
|||
|
|
public:
|
|||
|
|
PluginManager();
|
|||
|
|
~PluginManager();
|
|||
|
|
|
|||
|
|
// 禁止拷贝
|
|||
|
|
PluginManager(const PluginManager&) = delete;
|
|||
|
|
PluginManager& operator=(const PluginManager&) = delete;
|
|||
|
|
|
|||
|
|
/// 从目录加载所有插件
|
|||
|
|
int load_plugin_dir(const std::string& dir_path);
|
|||
|
|
|
|||
|
|
/// 加载单个插件(路径到 .so/.dylib/.dll)
|
|||
|
|
bool load_plugin(const std::string& plugin_path);
|
|||
|
|
|
|||
|
|
/// 卸载指定插件
|
|||
|
|
bool unload_plugin(const std::string& name);
|
|||
|
|
|
|||
|
|
/// 卸载所有插件
|
|||
|
|
void unload_all();
|
|||
|
|
|
|||
|
|
/// 获取已加载的插件
|
|||
|
|
PluginInterface* get_plugin(const std::string& name);
|
|||
|
|
|
|||
|
|
/// 列出所有已加载插件
|
|||
|
|
std::vector<std::string> list_plugins() const;
|
|||
|
|
|
|||
|
|
/// 检查插件是否已加载
|
|||
|
|
bool is_loaded(const std::string& name) const;
|
|||
|
|
|
|||
|
|
/// 获取加载错误信息
|
|||
|
|
std::string last_error() const;
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
struct Impl;
|
|||
|
|
std::unique_ptr<Impl> impl_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} // namespace vde::plugin
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 注册/加载函数
|
|||
|
|
|
|||
|
|
每个插件动态库必须导出以下 C 链接符号:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 创建插件实例(必须导出)
|
|||
|
|
extern "C" VDE_PLUGIN_EXPORT vde::plugin::PluginInterface* create_plugin();
|
|||
|
|
|
|||
|
|
// 销毁插件实例(必须导出)
|
|||
|
|
extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(vde::plugin::PluginInterface* plugin);
|
|||
|
|
|
|||
|
|
// 获取插件清单(可选,优化加载速度)
|
|||
|
|
extern "C" VDE_PLUGIN_EXPORT const vde::plugin::PluginManifest* get_manifest();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 插件开发
|
|||
|
|
|
|||
|
|
### 4.1 最小插件示例
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// my_exporter.cpp
|
|||
|
|
#include <vde/plugin/plugin_system.h>
|
|||
|
|
#include <iostream>
|
|||
|
|
|
|||
|
|
using namespace vde::plugin;
|
|||
|
|
|
|||
|
|
class MyExporter : public PluginInterface {
|
|||
|
|
public:
|
|||
|
|
bool initialize() override {
|
|||
|
|
manifest_.name = "io.export.myformat";
|
|||
|
|
manifest_.version = "1.0.0";
|
|||
|
|
manifest_.author = "Your Name";
|
|||
|
|
manifest_.description = "Export to MyFormat";
|
|||
|
|
manifest_.type = PluginManifest::Type::IO_EXPORT;
|
|||
|
|
manifest_.vde_version_min = "6.0.0";
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void shutdown() override {
|
|||
|
|
// 清理资源
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const PluginManifest& manifest() const override {
|
|||
|
|
return manifest_;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const char* name() const override {
|
|||
|
|
return manifest_.name.c_str();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const char* version() const override {
|
|||
|
|
return manifest_.version.c_str();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool execute(const std::string& action,
|
|||
|
|
const std::map<std::string, std::string>& params,
|
|||
|
|
void* input, void* output) override {
|
|||
|
|
if (action == "export") {
|
|||
|
|
// 执行导出逻辑
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
PluginManifest manifest_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 必需的导出符号
|
|||
|
|
extern "C" VDE_PLUGIN_EXPORT PluginInterface* create_plugin() {
|
|||
|
|
return new MyExporter();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(PluginInterface* plugin) {
|
|||
|
|
delete plugin;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 CMake 构建
|
|||
|
|
|
|||
|
|
```cmake
|
|||
|
|
# CMakeLists.txt for plugin
|
|||
|
|
cmake_minimum_required(VERSION 3.16)
|
|||
|
|
project(my_exporter VERSION 1.0.0)
|
|||
|
|
|
|||
|
|
find_package(ViewDesignEngine REQUIRED)
|
|||
|
|
|
|||
|
|
add_library(my_exporter SHARED my_exporter.cpp)
|
|||
|
|
target_link_libraries(my_exporter PRIVATE vde::engine)
|
|||
|
|
target_include_directories(my_exporter PRIVATE ${VDE_INCLUDE_DIRS})
|
|||
|
|
|
|||
|
|
# 设置输出到插件目录
|
|||
|
|
set_target_properties(my_exporter PROPERTIES
|
|||
|
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.3 插件类型
|
|||
|
|
|
|||
|
|
| 类型 | 枚举值 | 说明 | 示例 |
|
|||
|
|
|------|--------|------|------|
|
|||
|
|
| 格式导入器 | `IO_IMPORT` | 新增文件格式导入 | USDZ, 3DS, AMF |
|
|||
|
|
| 格式导出器 | `IO_EXPORT` | 新增文件格式导出 | USDZ, FBX, X3D |
|
|||
|
|
| 几何过滤器 | `GEOMETRY_FILTER` | 几何后处理 | 平滑、简化、重网格 |
|
|||
|
|
| 自定义工具 | `CUSTOM_TOOL` | 领域特定工具 | 应力分析、拓扑优化 |
|
|||
|
|
|
|||
|
|
## 5. 插件加载流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Application::init()
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
PluginManager::load_plugin_dir("/usr/lib/vde/plugins")
|
|||
|
|
│
|
|||
|
|
├── 扫描目录: for each *.so / *.dylib / *.dll
|
|||
|
|
│
|
|||
|
|
├── dlopen(plugin_path, RTLD_NOW)
|
|||
|
|
│ │
|
|||
|
|
│ ├── 成功 → 继续
|
|||
|
|
│ └── 失败 → 记录错误,跳过
|
|||
|
|
│
|
|||
|
|
├── dlsym(handle, "get_manifest")
|
|||
|
|
│ │
|
|||
|
|
│ ├── 找到 → 读取 manifest,检查版本兼容性
|
|||
|
|
│ └── 未找到 → 继续加载(稍后从 initialize 获取)
|
|||
|
|
│
|
|||
|
|
├── dlsym(handle, "create_plugin")
|
|||
|
|
│ │
|
|||
|
|
│ ├── 找到 → PluginInterface* plugin = create_plugin()
|
|||
|
|
│ └── 未找到 → dlclose,跳过
|
|||
|
|
│
|
|||
|
|
├── plugin->initialize()
|
|||
|
|
│ │
|
|||
|
|
│ ├── 返回 true → 注册到 Registry
|
|||
|
|
│ └── 返回 false → destroy_plugin + dlclose
|
|||
|
|
│
|
|||
|
|
└── 返回加载成功数量
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 版本兼容性检查
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 伪代码
|
|||
|
|
bool is_compatible(const PluginManifest& m, const std::string& vde_version) {
|
|||
|
|
// 检查最小版本
|
|||
|
|
if (compare_versions(vde_version, m.vde_version_min) < 0)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
// 检查最大版本(如果指定)
|
|||
|
|
if (!m.vde_version_max.empty() &&
|
|||
|
|
compare_versions(vde_version, m.vde_version_max) > 0)
|
|||
|
|
return false;
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 生命周期管理
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────────────────────────────┐
|
|||
|
|
│ 插件生命周期 │
|
|||
|
|
│ │
|
|||
|
|
│ [编译] → [发现] → [加载] → [初始化] → [运行] → [卸载] │
|
|||
|
|
│ │
|
|||
|
|
│ 编译: 开发者编写代码,构建为 .so/.dylib/.dll │
|
|||
|
|
│ 发现: PluginManager 扫描插件目录 │
|
|||
|
|
│ 加载: dlopen 加载动态库 │
|
|||
|
|
│ 初始化: initialize() 注册功能 │
|
|||
|
|
│ 运行: execute() 被调用 │
|
|||
|
|
│ 卸载: shutdown() + dlclose │
|
|||
|
|
│ │
|
|||
|
|
│ 状态机: │
|
|||
|
|
│ UNLOADED → LOADING → INITIALIZED → RUNNING │
|
|||
|
|
│ ↑ ↓ ↓ │
|
|||
|
|
│ └── ERROR SHUTTING_DOWN → UNLOADED│
|
|||
|
|
└──────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 状态说明
|
|||
|
|
|
|||
|
|
| 状态 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `UNLOADED` | 插件未加载或已卸载 |
|
|||
|
|
| `LOADING` | dlopen 成功,正在初始化 |
|
|||
|
|
| `INITIALIZED` | initialize() 完成,等待 execute |
|
|||
|
|
| `RUNNING` | execute() 正在执行中 |
|
|||
|
|
| `SHUTTING_DOWN` | shutdown() 执行中 |
|
|||
|
|
| `ERROR` | 加载或初始化失败 |
|
|||
|
|
|
|||
|
|
## 7. 清单文件
|
|||
|
|
|
|||
|
|
插件可以在同目录放置 `plugin.json` 描述文件,加快发现速度:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"name": "io.export.usdz",
|
|||
|
|
"version": "1.0.0",
|
|||
|
|
"author": "Your Name <email@example.com>",
|
|||
|
|
"description": "Export geometry to USDZ format for Apple AR",
|
|||
|
|
"license": "MIT",
|
|||
|
|
"type": "IO_EXPORT",
|
|||
|
|
"vde_version_min": "6.0.0",
|
|||
|
|
"vde_version_max": "",
|
|||
|
|
"dependencies": [],
|
|||
|
|
"library": "libvde_usdz_export.so",
|
|||
|
|
"entry_points": {
|
|||
|
|
"create": "create_plugin",
|
|||
|
|
"destroy": "destroy_plugin"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### PluginManager 加载优先级
|
|||
|
|
|
|||
|
|
1. 如果存在 `plugin.json` → 先读取 JSON 验证兼容性
|
|||
|
|
2. 兼容 → `dlopen` + `dlsym(create_plugin)`
|
|||
|
|
3. 不兼容 → 跳过,记录日志
|
|||
|
|
4. 无 `plugin.json` → 直接 `dlopen` + 尝试 `get_manifest` 符号
|
|||
|
|
|
|||
|
|
## 8. 安全考虑
|
|||
|
|
|
|||
|
|
### 8.1 隔离性
|
|||
|
|
|
|||
|
|
| 措施 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| 独立 .so | 每个插件独立动态库 |
|
|||
|
|
| 崩溃隔离 | 插件 SIGSEGV 不传播到主程序(信号处理器) |
|
|||
|
|
| 符号可见性 | 插件使用 `-fvisibility=hidden` 隐藏内部符号 |
|
|||
|
|
| 内存隔离 | 插件通过 PluginInterface 与主程序交互,不直接访问内部数据 |
|
|||
|
|
|
|||
|
|
### 8.2 信任模型
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌───────────────────────────────────────┐
|
|||
|
|
│ 插件信任级别 │
|
|||
|
|
│ │
|
|||
|
|
│ L0 内置: VDE 官方维护,完全信任 │
|
|||
|
|
│ L1 签名: 第三方已签名,受信任 │
|
|||
|
|
│ L2 社区: 社区插件,有限沙箱 │
|
|||
|
|
│ L3 未知: 未签名/未知来源,严格沙箱 │
|
|||
|
|
│ │
|
|||
|
|
│ L3 限制: │
|
|||
|
|
│ - 不能访问文件系统(除了输出目录) │
|
|||
|
|
│ - 不能调用网络 │
|
|||
|
|
│ - 不能 fork │
|
|||
|
|
│ - CPU/内存限制 │
|
|||
|
|
└───────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.3 最佳安全实践
|
|||
|
|
|
|||
|
|
1. **始终验证 manifest 版本兼容性**后再 initialize
|
|||
|
|
2. **dlopen 使用 `RTLD_NOW | RTLD_LOCAL`** 而非 `RTLD_LAZY | RTLD_GLOBAL`
|
|||
|
|
3. **设置超时**:execute() 调用应有超时机制
|
|||
|
|
4. **资源限制**:通过 cgroups/Docker 限制插件资源
|
|||
|
|
5. **日志审计**:记录所有插件加载/卸载/执行事件
|
|||
|
|
|
|||
|
|
## 9. 最佳实践
|
|||
|
|
|
|||
|
|
### 9.1 插件开发建议
|
|||
|
|
|
|||
|
|
- ✅ 插件尽量无状态,每次 execute 独立
|
|||
|
|
- ✅ 用 manifest 声明清晰的功能范围
|
|||
|
|
- ✅ 支持版本协商(`vde_version_min` / `vde_version_max`)
|
|||
|
|
- ✅ 提供详细的错误信息(通过返回值 + 日志)
|
|||
|
|
- ✅ 清理所有资源在 shutdown() 中
|
|||
|
|
- ❌ 不要在 initialize() 中做耗时操作
|
|||
|
|
- ❌ 不要依赖全局可变状态
|
|||
|
|
- ❌ 不要修改 VDE 核心数据结构
|
|||
|
|
|
|||
|
|
### 9.2 插件命名规范
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
格式: <category>.<subcategory>.<name>
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
- io.import.fbx # FBX 格式导入
|
|||
|
|
- io.export.usdz # USDZ 格式导出
|
|||
|
|
- geometry.smooth.laplacian # Laplacian 平滑
|
|||
|
|
- tool.analysis.stress # 应力分析工具
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 9.3 使用示例
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/plugin/plugin_system.h>
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
vde::plugin::PluginManager mgr;
|
|||
|
|
|
|||
|
|
// 加载所有插件
|
|||
|
|
int count = mgr.load_plugin_dir("/usr/lib/vde/plugins");
|
|||
|
|
std::cout << "Loaded " << count << " plugins\n";
|
|||
|
|
|
|||
|
|
// 列出已加载插件
|
|||
|
|
for (auto& name : mgr.list_plugins()) {
|
|||
|
|
std::cout << " - " << name << "\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用特定插件
|
|||
|
|
auto* exporter = mgr.get_plugin("io.export.gltf");
|
|||
|
|
if (exporter) {
|
|||
|
|
std::map<std::string, std::string> params;
|
|||
|
|
params["path"] = "output.glb";
|
|||
|
|
params["binary"] = "true";
|
|||
|
|
|
|||
|
|
void* mesh_data = /* ... */;
|
|||
|
|
bool ok = exporter->execute("export", params, mesh_data, nullptr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 卸载
|
|||
|
|
mgr.unload_all();
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 附录: 平台差异
|
|||
|
|
|
|||
|
|
| 操作 | Linux | macOS | Windows |
|
|||
|
|
|------|-------|-------|---------|
|
|||
|
|
| 动态库后缀 | `.so` | `.dylib` | `.dll` |
|
|||
|
|
| 加载函数 | `dlopen` | `dlopen` | `LoadLibraryA` |
|
|||
|
|
| 符号解析 | `dlsym` | `dlsym` | `GetProcAddress` |
|
|||
|
|
| 卸载函数 | `dlclose` | `dlclose` | `FreeLibrary` |
|
|||
|
|
| 错误信息 | `dlerror()` | `dlerror()` | `GetLastError()` |
|
|||
|
|
| 链接库 | `-ldl` | (内置) | `kernel32.lib` |
|
|||
|
|
| 可见性宏 | `__attribute__((visibility("default")))` | 同 Linux | `__declspec(dllexport)` |
|