11

我正在尝试编写一个在运行时动态加载其扩展的应用程序。我使用 Boost Preprocessor 库编写了一个预处理器函数,给定一个名称列表,为每个名称声明一个类(并使它们都成为某个 AbstractPlugin 类的子类),然后声明一个包含这些类的 Boost MPL 序列。然后我编写了一个类,它尝试一个指向 AbstractPlugin 的指针,如果它可以转换为该 MPL 序列中的任何类型。这里的问题是我的预处理器函数需要我想要创建和加载的所有扩展的完整列表。是否有一些技术可以让我在单独的文件中注册每个扩展?

更新:

我相信,我对情况的解释太模糊了,所以我决定把它说得更具体一些。

我想定义一个扩展类型的集合。对于每种扩展类型,可以有任意数量的扩展。在运行时,程序加载外部库,解析入口点函数,调用它,结果得到一个指针。然后它尝试将该指针强制转换为所有已注册的扩展类型(使用dynamic_cast, 因此扩展类型的类都继承自某个多态基类)。如果对某个扩展类型的转换成功,则转换后的指针用于调用该扩展类型的特殊处理程序。

扩展类型的数量在编译时是已知的(显然,扩展的数量是无限的)。使用我的方法,加载器类使用这些知识来检查每个扩展类型是否存在处理程序(如果不存在,则程序无法编译)。此外,我的方法并不强制扩展类型的类对加载器有任何了解(因此修改加载器很容易)。但是如果每个扩展类型都注册自己会更方便。

4

3 回答 3

13

您可以让您的所有课程在某种集合中自行注册。这是一个骨架方法:

基础.hpp:

#include <memory>
#include <unordered_map>
#include <string>

struct Base
{
    virtual ~Base() = default;

    using create_f = std::unique_ptr<Base>();

    static void registrate(std::string const & name, create_f * fp)
    {
        registry()[name] = fp;
    }

    static std::unique_ptr<Base> instantiate(std::string const & name)
    {
        auto it = registry().find(name);
        return it == registry().end() ? nullptr : (it->second)();
    }

    template <typename D>
    struct Registrar
    {
        explicit Registrar(std::string const & name)
        {
            Base::registrate(name, &D::create);
        }
        // make non-copyable, etc.
    };

private:
    static std::unordered_map<std::string, create_f *> & registry();
};

基础.cpp:

#include "Base.hpp"

std::unordered_map<std::string, Base::create_f *> & Base::registry()
{
    static std::unordered_map<std::string, Base::create_f *> impl;
    return impl;
}

现在在客户端中使用它:

派生的.hpp:

#include "Base.hpp"

struct Derived : Base
{
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); }
    // ...
};

派生的.cpp:

#include "Derived.hpp"

namespace
{
    Base::Registrar<Derived> registrar("MyClass");
}

的构造函数负责在 name 下Base::Registrar<Derived>注册类。您可以通过以下方式动态创建实例:Derived"MyClass"Derived

std::unique_ptr<Base> p = Base::instantiate("MyClass");

可以/应该通过检测重复注册、打印可用类列表等来改进代码。注意我们如何避免任何静态初始化排序问题,我将实际的注册表映射对象设为块静态对象,保证在之前初始化它的第一次使用,因此只有在最后一次使用后才被销毁。

于 2012-06-24T09:13:58.200 回答
0

使用抽象工厂模式来实现这样的扩展框架并不难。

http://en.wikipedia.org/wiki/Abstract_factory_pattern

您可以在全局列表中注册这些抽象工厂函数/对象,并在此基础上做任何您想做的事情。

于 2012-06-24T07:40:54.600 回答
-1

事实证明,我想要的是不可能的。原因是在这种情况下“注册”意味着“将类型放入类型序列中”并且类型序列是不可变的,因为它们本身就是类型。因此,应该手动创建这种类型序列,或者像某些人建议的那样将“注册”移动到运行时。

于 2016-04-06T10:08:21.277 回答