Skip to content

luyoungcn/plugin_framework

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Plugin System Demo — C++ 插件化系统演示项目

一个完整、开箱即用的 C++ 插件化系统 Demo,演示了动态库导出、工厂模式、Magic Static、生命周期钩子等核心知识点。

项目简介

本项目实现了一个最小但完整的 C++ 插件化系统。宿主程序在运行时动态加载共享库(.so),通过工厂接口创建插件实例并调用其功能,展示了现代 C++ 与操作系统动态链接机制的深度结合。

涉及的核心 C++ 知识点

# 知识点 代码位置 说明
1 动态库导出宏与 extern "C" include/plugin_core.h PSDK_PLUGIN_EXPORT_C 宏实现跨平台符号导出,阻止 C++ Name Mangling
2 工厂模式 + 反射雏形 include/plugin_core.h + plugin/my_plugin.cpp std::map<string, function> 注册表实现按字符串名动态创建类实例
3 Magic Static + Lambda IIFE plugin/my_plugin.cpp static T obj = [](){}() 模式确保线程安全、延迟初始化的单例工厂
4 动态库生命周期钩子 plugin/my_plugin.cpp 全局对象的构造函数/析构函数自动在 dlopen/dlclose 时执行

目录结构

plugin_system_demo/
├── CMakeLists.txt                # 现代 CMake 构建配置
├── README.md                     # 项目文档(本文)
├── include/
│   └── plugin_core.h              # 核心接口定义(IPlugin / IPluginFactory / 导出宏)
├── plugin/
│   └── my_plugin.cpp             # 插件实现(DataAggregatorPlugin + 入口函数 + 生命周期钩子)
└── host/
    └── main.cpp                  # 宿主程序(dlopen/dlsym/dlclose + 工厂调用演示)

编译指导

环境要求

  • C++ 编译器:GCC 7+ / Clang 5+ / MSVC 2019+
  • 构建工具:CMake 3.14+
  • 操作系统:Linux / macOS / WSL2 / Windows (WSL)

编译步骤(Linux / macOS / WSL)

# 1. 进入项目根目录
cd plugin_system_demo

# 2. 创建构建目录(推荐 out-of-source 构建)
mkdir -p build && cd build

# 3. 配置 CMake(生成 Makefile)
cmake ..

# 4. 编译
make

# 5. 查看编译产物
ls -la bin/ lib/

运行指导与输出分析

运行步骤

# 确保当前目录为 build/bin(或将 lib 加入 LD_LIBRARY_PATH)
cd build/bin

# 运行宿主程序
./host_app

期望输出结果

============================================
  Plugin System Demo - Host Application
============================================

[Host] Loading plugin library: ./libmyplugin.so
[data_proc] Shared library data_proc plugin loaded OK          <-- dlopen() 触发全局构造
[Host] Factory loaded: name='PluginSystem', version='1.0.0'
[Host] Creating plugin instance: DataAggregatorPlugin
[Host] Plugin info: name='DataAggregatorPlugin', version='1.0.0'

[Host] Initializing plugin...
[DataAggregator] Initialized with 10 data points.

[Host] Executing plugin...
========== [DataAggregator] Statistics Report ==========
  Dataset size : 10
  Mean         : 55.00
  Median       : 55.00
  Min          : 10.00
  Max          : 100.00
======================================================

[Host] Shutting down plugin...
[DataAggregator] Shutdown complete.

[Host] Cleaning up...
[Host] Closing plugin library...

[data_proc] Shared library data_proc plugin unloaded OK        <-- dlclose() 触发全局析构

[Host] Done.

输出时机说明

输出行 触发时机 说明
"[data_proc] Shared library data_proc plugin loaded OK" dlopen() 成功返回后 动态库被映射到进程空间,全局对象 _DLLInit 构造函数执行
"Factory loaded: ... GetPluginFactory() 被调用后 工厂实例的 Magic Static 初始化完成并返回地址
"Initialized with ... data points" plugin->initialize() DataAggregatorPlugin 初始化内部数据集
"Statistics Report" plugin->execute() 数据聚合逻辑执行完毕
"Shutdown complete" plugin->shutdown() 插件释放资源
"[data_proc] Shared library data_proc plugin unloaded OK" dlclose() 被调用后 库的引用计数归零,库从进程空间卸载,全局对象析构

核心实现解析

1. 动态库导出宏

#if defined(_WIN32) || defined(_WIN64)
    #define PSDK_PLUGIN_EXPORT_C extern "C" __declspec(dllexport)
#else
    #define PSDK_PLUGIN_EXPORT_C extern "C" __attribute__((visibility("default")))
#endif
  • extern "C":阻止 C++ Name Mangling,使符号以纯 C 方式编译/链接
  • __attribute__((visibility("default"))):GCC/Clang 下强制将符号标记为导出可见
  • __declspec(dllexport):MSVC 下导出动态库符号

2. 工厂模式 + 反射雏形

// 注册阶段(插件代码)
p.registerClass<DataAggregatorPlugin>("DataAggregatorPlugin");

// 调用阶段(宿主代码)——无需知道 DataAggregatorPlugin 的具体类型
auto plugin = factory->createInstance("DataAggregatorPlugin");

通过 std::map<std::string, std::function<...>> 实现的注册表,使宿主程序可以完全通过字符串名称来请求任意已���册的插件类型,无需编译期耦合。

3. Magic Static + Lambda IIFE

static PluginFactory pinfo = []
{
    auto p = PluginFactory("data_proc", "1.0.0");
    p.registerClass<DataAggregatorPlugin>("DataAggregatorPlugin");
    return p;
}();
  • static 局部变量保证延迟初始化(直到第一次调用时才创建)
  • C++11 起标准保证静态局部变量初始化线程安全
  • Lambda 立即执行表达式([](){}())使初始化逻辑内联、紧凑

4. 动态库生命周期钩子

struct _DLLInit
{
    _DLLInit()  { std::cout << "plugin loaded OK" << std::endl; }
    ~_DLLInit() { std::cout << "plugin unloaded OK" << std::endl; }
} dll_init;

C++ 全局对象的构造函数在动态库被 dlopen() 时自动调用,析构函数在 dlclose() 时自动调用,无需宿主程序显式介入。


扩展思路

  • 多插件支持:在 PluginFactory::registry_ 中注册多个类,通过不同的字符串名称分别创建
  • Windows 支持:添加 host/main_win.cpp,使用 LoadLibrary / GetProcAddress / FreeLibrary
  • 插件版本管理:在注册表中记录每个类的版本号,宿主程序验证兼容性后再使用
  • 依赖注入:在 initialize() 中传入宿主程序提供的上下文对象或回调函数
  • 热加载:使用 dlclose + 重新 dlopen 实现插件的动态替换(需处理好对象生命周期)

许可证

本项目采用 MIT 许可证,可自由使用、修改和分发。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors