C++26静态反射无法用于插件系统运行时类型发现,因其仅在编译期生效,dlopen后无元数据;跨DLL边界必须依赖extern "C"导出、纯虚接口指针及手动类型映射,且接口需POD-like、禁用STL/异常/RTTI以保障ABI稳定。

不能靠 C++26 静态反射实现插件系统的运行时类型发现——它编译期就结束了,dlopen 后什么也看不到。 真正能跨 DLL 边界工作的,只有显式导出的 extern "C" 函数 + 纯虚接口指针 + 手动类型映射。别被“反射”二字带偏,这里没魔法,只有契约和约束。
为什么 dlsym / GetProcAddress 找不到类成员函数
因为 C++ 类名经过 name mangling 后符号不可预测,dlsym 只认 C 风格未修饰符号。MyPlugin::create() 这种写法在动态库中根本不存在对应符号;即使你用 extern "C" 包裹,返回的仍是 void*,不是 MyPlugin*。
- 错误现象:
dlsym(handle, "create_plugin")成功,但static_cast<myplugin>(ptr)</myplugin>后调用虚函数崩溃或读取成员乱码 - 根本原因:主程序与插件用了不同编译器、不同 STL 实例、不同 ABI 版本,
sizeof(MyPlugin)或虚表布局不一致 - 正确做法:只导出
extern "C"工厂函数,返回抽象接口指针(如IPlugin*),且该接口必须是 POD-like、无虚继承、无非平凡成员 - 禁止在接口中使用
std::string、std::vector、异常、RTTI、模板特化——它们在 DLL 边界不可靠
如何设计一个跨平台稳定的插件接口
越“面向对象”越危险,越像 C 越可靠。虚函数表跨模块极易出问题,尤其 Windows 上 MSVC 的 ABI 不保证二进制兼容。
- 推荐方案:定义纯 C 接口结构体,含函数指针表,例如
struct PluginAPI { void* (*create)(); void (*destroy)(void*); int (*process)(void*, const char*); }; - 字符串统一用
const char*,内存所有权必须明确:插件分配、主程序释放(或反之),并在文档中写死 - 若需类型识别,插件导出
const char* plugin_type_name(),主程序用字符串比对,而非依赖typeid或dynamic_cast - Qt 场景下可用
QPluginLoader+QObject子类,但前提是插件和主程序共用同一套 Qt 构建配置(否则qobject_cast失败)
LoadLibrary / dlopen 失败的 90% 原因不是代码,而是路径与依赖
别急着改 create_plugin 实现,先确认 DLL 能否独立加载。90% 的失败源于依赖链断裂或路径污染。
立即学习“C++免费学习笔记(深入)”;
- Windows 下用
Dependencies.exe(不是旧版 Dependency Walker)打开插件 DLL,看红色标记的缺失模块——尤其是MSVCP140.dll、VCRUNTIME140_1.dll - 主程序用
/MD编译,插件却用/MDd?调试版运行时不随发布包分发,必然失败 - 调用
LoadLibrary前加一句SetDllDirectory(L"."),强制只从插件同目录搜索依赖,避免系统目录里的旧版 DLL 干扰 - 传给
LoadLibrary的路径必须是绝对路径:GetFullPathName(L"plugins\ai.dll", ...)再传入,相对路径在工作目录变更后必崩
插件热重载后状态残留的典型表现与修复
卸载再加载插件后,新插件初始化失败、日志重复打印、第三方 SDK 报 “already initialized” —— 基本可以断定是静态变量没清理。
- 插件 DLL 内禁止使用
static std::map<int std::string> g_cache;</int>,改用PluginInterface实例的成员变量 - 所有第三方库(如
spdlog::default_logger()、FMOD::System::create())必须在shutdown()中显式释放,不能靠 DLL 卸载自动析构 - 主程序在调用
FreeLibrary前,必须确保插件已调用destroy_plugin(),且所有资源已归还
最易被忽略的一点:插件和主程序之间没有“类型共识”,只有“符号共识”和“内存布局共识”。哪怕接口定义一字不差,只要两边编译选项稍有差异(比如一个开了 /EHsc 一个没开),虚表偏移就可能错位——这种问题不会报编译错误,只会静默崩溃。


















