模板类的工厂方法 [英] Factory method for template classes

查看:88
本文介绍了模板类的工厂方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在尝试建立工厂功能时遇到了一个问题, 给定一个ID和一个类型,它将返回正确的(模板化)子类.

I have an issue I'm facing where I'm trying to build a factory function that, given an ID and a type will return the correct (templated) subclass.

这是要解决的问题:

id()值在建立连接后立即通过网络发送,并向接收者指定如何对字节序列进行编码.接收者预先知道它期望的类型T,但是直到它获得该值之前,它不知道该类型T如何在线路上编码.它还指定返回时如何整理返回值(某种类型的U,其中U可能与T的类型相同或不同).通常使用此代码,即有多个使用/预期不同类型的发送者/接收者;但是,给定发送方/接收方对之间使用的类型始终是固定的.

The id() values are sent across a network as soon as a connection is established, and specify to the receiver how a sequence of bytes are encoded. The receiver knows in advance the type T that it expects, but does not know how that type T is encoded on the wire until it gets this value. It also specifies how return values (of some type U, where U may or may not be the same type as T) should be marshalled when they are returned. This code is used generally, i.e. there are multiple senders/receivers that use/expect different types; the types used between a given sender/receiver pair are always fixed, however.

问题的基本示意图:我们有一个(简化的)基类,定义了id()

A basic sketch of the problem: we have a (simplified) base class that defines id()

template <typename T>
class foo
{
public:

    virtual ~foo() { }

    // Other methods

    // This must return the same value for every type T
    virtual std::uint8_t id() const noexcept = 0;
};

从那里开始,我们有一些子类:

From there, we have some subclasses:

template <typename T>
class bar : public foo<T>
{
public:
    std::uint8_t id() const noexcept override { return 1; }
};

template <typename T>
class quux : public foo<T>
{
public:
    std::uint8_t id() const noexcept override { return 2; }
};

对于实际的工厂功能,我需要存储一些 删除类型(例如bar,quux),以便我可以存储实际 均质容器中的创建函数. 实际上,我想要的语义大致等同于:

For the actual factory function, I need to store something that erases the type (e.g. bar, quux) so that I can store the actual creation function in a homogenous container. Effectively, I want semantics that are roughly equivalent to:

struct creation_holder
{
    // Obviously this cannot work, as we cannot have virtual template functions
    template <typename T>
    virtual foo<T>* build() const;
};

template <template <typename> class F>
struct create : public creation_holder
{
    // As above
    template <typename T>
    foo<T>* build() const override
    {
        return new F<T>();
    }
};

std::unordered_map<std::uint8_t, create*>& mapping()
{
    static std::unordered_map<std::uint8_t, create*> m;
    return m;
}

template <typename T, template <typename> class F>
bool register_foo(F<T> foo_subclass,
    typename std::enable_if<std::is_base_of<foo<T>, F<T>>::value>::type* = 0)
{
    auto& m = mapping();
    const auto id = foo_subclass.id();
    creation_holder* hold = new create<F>();
    // insert into map if it's not already present
}

template <typename T>
foo<T>* from_id(std::uint8_t id)
{
    const auto& m = mapping();
    auto it = m.find(id);
    if(it == m.end()) { return nullptr; }
    auto c = it->second;
    return c->build<T>();
}

我尝试了很多方法来尝试获得类似的东西 语义,但没有运气.有没有办法做到这一点(我不在乎 实施方式有很大不同).

I've played around with a number of ideas to try and get something with similar semantics, but with no luck. Is there a way to do this (I don't care if the implementation is significantly different).

推荐答案

一些用于传递类型和类型束的实用程序类型:

Some utility types for passing around types and bundles of types:

template<class...Ts>
struct types_t {};
template<class...Ts>
constexpr types_t<Ts...> types{}; // C++14.  In C++11, replace types<T> with types_t<T>{}.  Then again, I don't use it.
template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag{}; // C++14.  In C++11, replace tag<T> with tag_t<T>{} below

现在我们写一个多工厂.

Now we write a poly ifactory.

这里是ifactory:

template<template<class...>class Z, class T>
struct ifactory {
  virtual std::unique_ptr<Z<T>> tagged_build(tag_t<T>) const = 0;
  virtual ~ifactory() {}
};

您传入要构建的标签,然后得到一个对象.很简单.

you pass in the tag you want to build and you get out an object. Pretty simple.

然后将它们捆绑在一起(在 1 ,但您要求输入

实时示例.

伪代码设计.

该界面有一个

template<class T> R build

功能.派遣到

virtual R tagged_build(tag_t<T>) = 0;

方法.

有问题的T是从types_t<Ts...>列表中提取的.仅支持这些类型.

The Ts in question are extracted from a types_t<Ts...> list. Only those types are supported.

在实现方面,我们创建CRTP帮助器的线性继承.每个继承自最后一个,并覆盖virtual R tagged_build(tag_t<T>).

On the implementation side, we create a linear inheritance of CRTP helpers. Each inherits from the last, and overrides a virtual R tagged_build(tag_t<T>).

tagged_build的实现使用CRTP将this指针强制转换为更派生的类,并在其上调用build_impl(tag<T>).这是非运行时多态的一个示例.

The implementation of tagged_build uses CRTP to cast the this pointer to a more-derived class and call build_impl(tag<T>) on it. This is an example of non-runtime polymorphism.

因此呼叫从build<T>转到virtual tagged_build(tag_t<T>)build_impl(tag<T>).用户只需与一个模板进行交互;实现者仅实现一个模板.中间的胶水-virtual tagged_build-是从types_t类型列表生成的.

So calls go build<T> to virtual tagged_build(tag_t<T>) to build_impl(tag<T>). Users just interact with one template; implementors just implement one template. The glue in the middle -- the virtual tagged_build -- is generated from a types_t list of types.

这是大约100行的胶水"或帮助程序代码,作为交换,我们获得了有效的虚拟模板方法.

This is about 100 lines of "glue" or helper code, and in exchange we get effectively virtual template methods.

1 "tag"> c ++ 17 变为:

1 in c++17 this becomes:

template<template<class...>class Z, class...Ts>
struct poly_ifactory_impl<Z,types_t<Ts...>>:
  ifactory<Z, Ts>...
{
  using ifactory<Z, Ts>::tagged_build...;
};

这更加简单明了.

最后,您可以像这样含糊地做某事,而无需中央类型列表.如果您知道调用方和被调用方都知道类型,则可以将typeidtypeindex传递给ifactory,将void*或类似内容传递给虚拟调度机制,然后强制转换/检查是否为null/在地图中查找类型.

Finally, you can do something vaguely like this without a central list of types. If you know both the caller and the callee know the type you could pass a typeid or typeindex into the ifactory, pass a void* or something similar out over the virtual dispatch mechanism, and cast/check for null/do a lookup in a map to types.

内部实现与此类似,但是您不必将types_t作为正式(或二进制)接口的一部分发布.

The internal implementation would look similar to this one, but you wouldn't have to publish types_t as part of your formal (or binary) interface.

在外部,您将不得不仅知道"支持哪些类型.在运行时,如果传入不受支持的类型,您可能会得到一个空的智能(或哑巴,ick)指针.

Externally, you would have to "just know" what types are supported. At runtime, you might get a null smart (or dumb, ick) pointer out if you pass in an unsupported type.

稍加注意,您甚至可以同时进行这两个操作.公开一种有效,安全的机制,以将编译时已知的类型应用于模板.还公开一个基于"try"的接口,该接口既使用高效的编译时已知系统(如果类型匹配),又退回到已检查的低效率运行时.您之所以这样做可能是出于深奥的向后二进制兼容性的原因(因此新软件可以通过过时的接口连接到新的或旧的API实现,并动态地处理旧的API实现).

With a bit of care you could even do both. Expose an efficient, safe mechanism to get compile-time known types applied to a template. Also expose a "try" based interface that both uses the efficient compile-time known system (if the type matches) and falls back on the inefficient runtime checked on. You might do this for esoteric backwards binary compatibility reasons (so new software can connect over an obsolete interface to new or old API implementations and handle having an old API implementation dynamically).

但是到那时,您是否考虑过使用COM?

But at that point, have you considered using COM?

这篇关于模板类的工厂方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆