注册一个C++类,以便以后函数可以迭代所有已注册的类

gri*_*ras 5 c++ metaprogramming registration boost-preprocessor

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

更新:

我相信,我对情况的解释太模糊了,所以我决定让它更具体.

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

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

Ker*_* SB 9

您可以使所有类在某种集合中自行注册.这是一个骨架方法:

Base.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();
};
Run Code Online (Sandbox Code Playgroud)

Base.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;
}
Run Code Online (Sandbox Code Playgroud)

现在在客户端中使用它:

Derived.hpp:

#include "Base.hpp"

struct Derived : Base
{
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); }
    // ...
};
Run Code Online (Sandbox Code Playgroud)

Derived.cpp:

#include "Derived.hpp"

namespace
{
    Base::Registrar<Derived> registrar("MyClass");
}
Run Code Online (Sandbox Code Playgroud)

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

std::unique_ptr<Base> p = Base::instantiate("MyClass");
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案有一个微妙的问题.Derived.cpp中定义的`registrar`不能保证由main中调用`base :: instantiate()`构造.只有在Derived.cpp转换单元使用时才能保证构造它. (2认同)
  • @Controller:注册是全局初始化的结果。然而,未指定这是否真的有效,因为初始化程序不需要运行,直到使用其翻译单元中的某些内容,而这里没有发生这种情况。实际上,此代码之所以有效,是因为供应商知道用户依赖它,但并不要求它按照标准工作。链接时需要小心,以确保链接器不会丢弃 TU。 (2认同)

gri*_*ras -1

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