C++ 从共享库加载类时基/派生类的安全性
我目前正试图在我的应用程序中内置一个基本的插件系统。理想情况下,我不想知道插件的类信息,因此在获取适当的内存管理函数时,我会使用基本C++ 从共享库加载类时基/派生类的安全性,c++,dlopen,C++,Dlopen,我目前正试图在我的应用程序中内置一个基本的插件系统。理想情况下,我不想知道插件的类信息,因此在获取适当的内存管理函数时,我会使用基本插件类,如下所示: void* handle = nullptr; if (!(handle = dlopen(path.c_str(), RTLD_LOCAL | RTLD_NOW))) { throw std::runtime_error("Failed to load library: " + path); } using allo
插件类,如下所示:
void* handle = nullptr;
if (!(handle = dlopen(path.c_str(), RTLD_LOCAL | RTLD_NOW))) {
throw std::runtime_error("Failed to load library: " + path);
}
using allocClass = Plugin *(*)();
using deleteClass = void (*)(Plugin *);
auto allocFunc = reinterpret_cast<allocClass>(
dlsym(handle, allocClassSymbol.c_str()));
auto deleteFunc = reinterpret_cast<deleteClass>(
dlsym(handle, deleteClassSymbol.c_str()));
if (!allocFunc || !deleteFunc) {
throw std::runtime_error("Allocator or deleter not found");
}
return std::shared_ptr<Plugin>(
allocFunc(),
[deleteFunc](Plugin *p){ deleteFunc(p); }
);
我的问题基本上是关于这种类型不匹配的安全性——插件使用自己的类型,但加载程序将其声明为基类型。从有限的测试来看,似乎没有什么问题,但我不确定它是否在删除插件部分的内存片,而不是派生部分的内存片
在应用程序不导入每个插件的标题的情况下,有没有更好的方法来实现这一点?抛开重新解释将void*
强制转换为函数指针的有效性不谈,通过不同类型的指针调用函数是非常困难的。类型是否相关并不重要,比如Plugin*(*)()
和TestPlugin*()
。通过不同类型的指针访问一个类型的对象也是很重要的,即使这些类型分别是派生的和基类的。比如说
Derived derived;
Base *base;
base = &derived; // OK, will access base subobject
base = reinterpret_cast<Base*>(&derived); // not OK, will access derived object
此实现要求插件具有虚拟析构函数。无论如何,这可能是个好主意。如果真的不需要deleter函数,客户端只需调用delete插件
。更好的方法是,从分配器返回一个共享的ptr,可能带有一个自定义的删除器。同时使用RTLD\u NOW和RTLD\u LAZY将生成0sense@SergeyA是的,这来自示例代码,我仍在努力解决这个问题,因为我以前从未进行过动态库加载。@n.m.dlsym
是一个POSIX函数,POSIX需要一个函数指针,可以完全强制转换到void*
并返回,请参阅“应用程序用法”在所提供的代码中,我看不到任何迹象表明TestPlugin
与Plugin
有任何关系。您应该回答您的问题以显示Plugin
和TestPlugin
之间的关系。可能需要注意的是Plugin
必须有一个虚拟析构函数。谢谢!至少修复起来相当简单。
Derived derived;
Base *base;
base = &derived; // OK, will access base subobject
base = reinterpret_cast<Base*>(&derived); // not OK, will access derived object
extern "C" {
Plugin *allocator() {
return new TestPlugin();
}
void deleter(Plugin *ptr) {
delete ptr;
}
}