模板化的自动工厂注册

  • 本文关键字:工厂 注册 c++ c++17
  • 更新时间 :
  • 英文 :


想象一下,我有一些对象都与某个接口基类相关。但是,所有这些对象都是由某个整数DIM(维度(模板化的。我假设所有基的导数都有一个静态成员int number()和一个静态字符串InputName。这个想法是注册这个静态方法number((。(实际上,每个类都有完整的静态接口。(

注册.hpp

template <int DIM>
class objectRegistry
{
public:
template<typename T>
Register() 
{
//something like interface_map[T::InputName] = T::number;
}
private:
static inline std::map<std::string, std::function<int ()>> interface_map;
}

收割台1.hpp

template <int DIM>
class base
{
public:
static inline const std::string InputName = "base";
static int number() { return 1; };
base(){};
};
//todo: Some Registration Here

收割台2.hpp

template <int DIM>
class derived : public base<DIM>, public AutomaticRegister<DIM, derived<DIM>>
{
public:
static inline std::string InputName = "derived";
static int number() { return 4; };
derived(){};
};
//todo: Some Registration Here

hearder3.hpp

template <int DIM, typename extra_type>
class derived2 : public base<DIM>, public AutomaticRegister<DIM, derived2<DIM,extra_type>>
{
public:
static inline std::string InputName  = "derived2 " + std::string(typeid(extra_type).name());
static int number() { return 5; };
extra_type member;
};
//todo: Some Registration Here

我可以为派生对象创建一个自动注册系统吗;理想情况下,它与对象定义存在于同一个头文件中。例如,我想要objectRegistry<3> 知道导出<3> ,derived2<3,int>,并且导出<3,double>存在。我尝试过这些方法:

C++类型在列表中自我注册的最佳方式?https://www.bfilipek.com/2018/02/factory-selfregister.html

然而,由于所有内容都隐藏在template< int DIM>中,因此它永远不会被实例化。

当用特定模板值实例化objectRegistry时,有没有一种方法可以强制实例化derived

链接到的两种方法中的任何一种都可以工作。

您的问题是使用的是类模板,而不是类。

如果你做了这个

class Something : public AutomaticRegister<Something>
{
// ...
};

那么您将获得自动注册表,因为Something是一个类。

您有类模板,它们根本不像一个类型。注册是通过实例化注册类来实现的,注册类是类模板的基类。

因此,为了实例化注册类,您需要将想要注册的东西视为类型。因此,您需要实例化类的某个部分,或者通过创建其中一个模板的实例。。。

derived2<1, double> d2_1_double;

或者通过显式实例化整个类模板。。。

template class derived2<1, double>;

或者通过显式实例化类模板的某个成员,比如数字函数。。。

template int derived2<1, double>::number();

或者通过创建一个实际的派生类。。。

struct d2_1_double : derived2<1, double> { };

或者从类模板中戳出类的某种其他方式。

但是,注册类模板的一个非常小的更改(添加类型成员别名(为我们提供了一种显式批量注册它们的机制,并且不需要从注册机制继承。

为了演示,我添加了一些非常简单的非生产质量代码。为此,我添加了一个非标准函数,为一个适用于gcc和clang的类型获取一个唯一的名称,但不知道其他编译器。这不是必要的,只是让我更容易。

#include <functional>
#include <iostream>
#include <string_view>
#include <unordered_map>
template <typename ... Ts> struct TypeList { };
template <typename T>
constexpr auto
fname()
{
return __PRETTY_FUNCTION__;
}
class Registry
{
std::unordered_map<std::string_view, std::function<int()>> map;
public:
void insert(std::string_view key, std::function<int()> value) {
assert(map.find(key) == map.end());
std::cout << "Register "" << key << "", " << value() << 'n';
map[key] = std::move(value);
}
int operator()(std::string_view key) const {
return map.at(key)();
}
};
template <int DIM>
Registry & registry()
{
static Registry result;
return result;
}

这是自动注册的内容,是你的一个链接中答案的修改版本。

template <typename T>
class AutoRegister
{
struct helper {
helper() { registry<T::dim>().insert(fname<T>(), T::number); }
};
/* inline */ static helper h;
template<helper&> struct ref { using type = AutoRegister; };
public:
using type = typename ref<h>::type;
};
// NOTE: A bug in gcc forces this usage rather than just using inline.
template <typename T>
typename AutoRegister<T>::helper AutoRegister<T>::h;

然后,使用一些与您类似的类模板。。。

template <int DIM>
struct Bar
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 99; }
};
template <int DIM, typename T>
struct Baz
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 86; }
};
template <int DIM, typename ... Ts>
struct Foo
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 42; }
};

和一个助手别名模板。。。

template <typename ... Ts>
using RegisterTypes = TypeList<typename AutoRegister<Ts>::type...>;

我们可以注册我们想要的东西。第二个有一些重复,只是为了表明这些东西只注册了一次。

using Registered = RegisterTypes<Bar<0>, Bar<1>, Baz<1>, Foo<1>>;
using Registered2 = RegisterTypes<Bar<2>, Bar<1>, Baz<1>, Foo<1>>;
int main()
{
}

运行程序会产生以下输出。。。

Register "auto fname() [T = Bar<0>]", 99
Register "auto fname() [T = Bar<1>]", 199
Register "auto fname() [T = Baz<1, int>]", 186
Register "auto fname() [T = Foo<1, int>]", 142
Register "auto fname() [T = Bar<2>]", 299
Register "auto fname() [T = Foo<1, int, double>]", 142```

最新更新