【问题标题】:Boost Plugin choicesBoost 插件选择
【发布时间】:2011-01-26 11:15:43
【问题描述】:

我希望在我们当前的应用程序(Unix、C++)中实现一个插件架构,并且一直在审查 Boost 库中的选项(待批准)。

编辑:我希望在运行时动态添加类。

Boost.Plugin

Boost.Reflection

Boost.Extension

我想知道大家对这些和其他实现的经验/意见是什么。

【问题讨论】:

  • 您针对未说明的问题发布了建议的解决方案。您有什么要求?

标签: c++ unix plugins boost


【解决方案1】:

嗯。我们刚刚使用了dlopendlsym 以及一对必须在dll 中定义的extern "C" static 函数

extern "C" static plugin* create( arg_pack* );
extern "C" static errno_t destroy( plugin* );

让插件管理器查找“.dll”或“.so”文件并将它们加载到
map<string, pair< plugin*(*)(arg_pack*), errno_t(*)(plugin*)> >

然后您可以根据名称(上面的字符串)查找插件并获取它的“构造函数”或“析构函数”

另请参阅:gmodule

【讨论】:

  • ABI/API 兼容性检查?
  • @Vlad ABI -> extern "C" 保证 c 风格的修改。 API -> dlsym 从库中加载所有相关函数。如果缺少任何内容,则它不是插件,您需要丢弃函数指针和 dlclose 库。
  • extern "C" 保证重整,但假设签名已更改,或者主机提供的任何接口已更改,并且插件期望不同的东西,最好的选择是崩溃,但后果可能非常严重(即来自交易系统的糟糕价格,这会使公司在几秒钟内破产)。应该有一套体面的检查,以确保满足任何预期的合同。这基本上应该是插件框架的一个主要目标,而不仅仅是包装 dlopen...
  • 如果没有反射,您就无法以您描述的方式对 API 做出保证。你能做的最好的事情就是传递map< string, pair< type_info, boost::any> > 之类的东西,让被调用者检查这个映射的所有参数。当然,被调用者可能恶意获取 0 个或 2 个或更多参数。我想您可以指定 cdecl 约定,以便调用者(您的代码)清理堆栈。我没有看到通过插入内联 asm 来防止被调用者对堆栈进行攻击的方法。
  • 您好,感谢您的回复。您所说的是当前的实现,但是我想重用现有代码而不是重新发明轮子。我查看了 gmodule,发现它很有见地,非常感谢。
【解决方案2】:

您不能在运行时加载类,因为 C++ 是一种编译语言,并且类在运行时不存在。对象(类的实例)可以。

您可以做的是在运行时加载共享库并让它们创建某个接口的对象。

最小的插件实现将定义插件的接口以及将使用该接口创建对象的工厂函数的接口。您将在运行时加载共享库,找到具有特定名称的工厂函数,并调用工厂函数来创建对象。然后通过接口使用该对象:

// plugin.h start
#include <memory>

struct PlugIn // interface
{
    virtual ~PlugIn() = 0;
    virtual void doSomething() = 0;
};

extern "C" {

typedef std::auto_ptr<PlugIn> PlugInFactoryFn();

// A plugin .so must export this one factory function.
std::auto_ptr<PlugIn> createPlugIn();

}
// plugin.h end

// somewhere in you application
#include "plugin.h"
#include <assert.h>
#include <dlfcn.h>

std::auto_ptr<PlugIn> loadPlugIn(char const* filename)
{
    void* so = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
    assert(so);
    void* factory_function = dlsym(so, "createPlugIn");
    assert(factory_function);
    return reinterpret_cast<PlugInFactoryFn*>(factory_function)();
}

int main()
{
    std::auto_ptr<PlugIn> a(loadPlugIn("a.so"));
    std::auto_ptr<PlugIn> b(loadPlugIn("b.so"));
    a->doSomething();
    b->doSomething();
}

【讨论】:

  • 现在,这在非 POSIX 平台上不起作用,当对象在插件的堆上分配并在主应用程序中删除时,可能会出现堆问题。更不用说 API/ABI 兼容性...
  • @Vlad:我们在这里谈论的是 Unix。
  • @Maxim:不过,您必须确保插件和主机是 ABI 兼容的(即相同或兼容的编译器等),以及 API 兼容。这就是为什么你需要一些更高级别的东西,比如 Boost,而使用牛仔 dlopen 是......你知道的。
  • @Vlad:取决于您想要支持的编译器。对于内部插件,上面的就好了。如果你想用不同的 ABI 支持不同的编译器,那么你需要 C 风格的 OO,但这超出了这个问题的范围。这里的主要思想是你的插件共享库不需要导出多个函数。
  • 这个想法很好解释。一般来说,多个入口点是没有意义的。
猜你喜欢
  • 2023-03-23
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 2012-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多