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;
    }
}