插件系统有五种跨模块通信方式:一、extern "C"导出工厂函数,用POD类型交互;二、纯虚接口+ABI稳定约定;三、函数指针表替代虚函数;四、RTTR库封装注册;五、Windows COM接口。

一、使用 extern "C" 导出工厂函数并手动类型映射
插件系统无法依赖 C++ 运行时反射,必须通过显式导出 C 风格符号来建立跨模块调用通道。主程序仅能通过 LoadLibrary/dlopen 获取模块句柄,再以 GetProcAddress/dlsym 查找函数地址;所有类型安全均由开发者保障,不可信任虚函数表或 dynamic_cast 跨 DLL 边界行为。
1、在插件 DLL 工程中定义纯 C 接口结构体,例如:struct PluginInterface { int (*init)(void*); void (*shutdown)(); int (*process)(const char*); };
2、实现工厂函数,并用 extern "C" __declspec(dllexport)(Windows)或 extern "C" __attribute__((visibility("default")))(Linux)修饰。
3、主程序加载 DLL 后,调用 GetProcAddress(L"create_interface") 获取函数指针,该函数返回 PluginInterface* 类型指针而非具体类实例。
4、调用返回接口中的函数指针执行逻辑,所有参数与返回值均限定为 POD 类型,禁止传递 std::string 或 std::vector。
二、基于纯虚接口 + ABI 稳定约定的跨模块对象传递
该方式将类型契约前移至头文件共享,要求插件与主程序使用完全一致的编译器、STL 版本及构建配置。虚函数调用虽简洁,但其可靠性高度依赖 ABI 二进制兼容性,在 Windows MSVC 或 GCC 不同版本间极易失效。
1、在公共头文件中声明抽象基类,如 class IPlugin { public: virtual ~IPlugin() = default; virtual int execute(const char*) = 0; };,不含虚继承、非平凡成员、RTTI 或异常。
2、插件 DLL 中实现该接口,并导出 extern "C" 工厂函数 extern "C" __declspec(dllexport) IPlugin* create_plugin();,内部完成 new 操作并返回基类指针。
3、主程序加载后调用 GetProcAddress 获取该函数地址,强制转换为 IPlugin*(*)() 类型并调用,获得多态对象指针。
4、使用完毕后,插件需提供匹配的 extern "C" __declspec(dllexport) void destroy_plugin(IPlugin*); 函数供主程序释放内存。
三、采用函数指针表(Function Pointer Table)替代虚函数机制
规避虚表布局不一致风险,将接口定义为结构体内嵌函数指针集合,所有调用均通过结构体字段间接跳转。此结构天然满足 POD 要求,可安全跨模块传递,且无 name mangling 干扰。
1、定义稳定接口结构体,如:struct PluginAPI { void* (*create)(); void (*destroy)(void*); int (*run)(void*, int, const char*); };
2、插件 DLL 导出单一符号 extern "C" __declspec(dllexport) const PluginAPI* get_plugin_api();,返回指向全局常量结构体的指针。
3、主程序调用 GetProcAddress(L"get_plugin_api") 获取函数地址,执行后得到 const PluginAPI* 指针。
4、后续所有操作均通过该结构体字段调用,例如:api->run(api->create(), 42, "data");,所有内存所有权规则在文档中明确约定。
四、利用 RTTR 库封装注册与发现流程
RTTR 提供编译期注册与运行时类型查询能力,可简化插件类型注册代码,但它不改变底层 DLL 加载约束——仍需 dlopen/LoadLibrary 显式加载,仍需 extern "C" 入口点启动注册过程,且 RTTR 必须在主程序与插件中静态链接以避免 ABI 冲突。
1、插件 DLL 实现注册函数:extern "C" __declspec(dllexport) void register_plugin(rttr::registration& reg);,内部调用 rttr::class_
2、主程序加载 DLL 后,调用 GetProcAddress 得到注册函数指针,并传入本地 rttr::registration 实例完成类型注册。
3、通过 rttr::type::get_by_name("MyPlugin") 查询已注册类型,再调用 type.create({rttr::variant("config")}) 构造实例。
4、所得 variant 对象需显式转换为原始指针或引用,auto ptr = var.get_value
五、Windows 平台专用:通过 COM 接口实现语言无关插件通信
COM 提供二进制稳定接口标准,支持跨语言、跨编译器调用,其 vtable 布局由 Windows SDK 严格定义,不受 C++ 编译器差异影响。插件需实现 IUnknown 及自定义接口,主程序通过 CoCreateInstance 或直接 QueryInterface 获取实例。
1、定义 IDL 文件描述插件接口,使用 midl.exe 编译生成头文件与类型库,确保插件与主程序引用同一份 .h 和 .tlb。
2、插件 DLL 实现 DllGetClassObject 导出函数,返回类厂对象,该对象支持创建指定 CLSID 的插件实例。
3、主程序调用 CoInitializeEx 初始化 COM,再调用 CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IPlugin), (void**)&pPlugin) 获取接口指针。
4、所有接口方法调用均经由 COM 调度器转发,内存管理由 AddRef/Release 控制,字符串统一使用 BSTR 或 LPCOLESTR,避免 STL 容器跨边界传递。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











