【问题标题】:Templated factory based on the union of different enum class values基于不同枚举类值联合的模板化工厂
【发布时间】:2018-11-12 10:01:14
【问题描述】:

我正在尝试基于用户可以选择编译时间的一些枚举值创建一种工厂。

这里的主要内容是创建一种神奇的开关,它适用于不止一种枚举的不同值(不同联合值的联合,可能具有相同的值)。

enum class A  {A1, A2, A3, A4};
enum class B  {B1, B2, B3, B4, B6, B7};

struct ObjectBase 
{
virtual void apply(char input) = 0;
};

template<A a, class... Args>
struct Object : ObjectBase;

template<>
struct Object<A::A1, int> : ObjectBase
{
Object(int i) i_(i) { }
void apply(char input) { /*do stuff with i_*/}
int i_;
}

template<>
struct Object<A::A2, int, double> : ObjectBase
{
Object(int i, double d) i_(i), d_(d) { }
void apply(char input) { /*do stuff with i_*/}
int i_;
double d_;
}


template<class V, V value>
struct Element 
{
};

template<class V, V first, V last>
struct AllElementWithin 
{
};

Switcher< Element<A,A1>, Element<A,A2>, 
  AllElementWithin<B, B1, B3> > switcher (2, 4.0);
// This should create a switch / lookup table that 
// initializes for instance Object<A::A1> with 2
// and Object<A::A2> with 2 and 4

char myInput = 'F';
ObjectBase* ob = switcher.create(A::A1); 
// This should return an ObjectBase* that points to a 
// Object<A1,int>

ob->apply(myInput);

我可以在这里利用一个已经知道的实现模式吗?我不想重新发明轮子。

最好是用 C++11 编译的东西

[编辑]更多信息:

工厂应该允许以有效的方式创建不同类型的对象(从特定的基类继承)。理想情况下,想要添加更多对象的用户类可以只创建他的枚举和一些定义所需行为的类,然后简单地使用带有这些枚举的工厂以及其他人定义的其他一些枚举。

如果不清楚,请要求其他说明

【问题讨论】:

  • 不知怎的,我觉得std::variant 值得一看。
  • 也许你应该稍微澄清一下这个问题——也许重命名类等等。目前尚不清楚这是您的目标是抽象工厂模式还是具体工厂模式。从您显示的代码和您的命名来看,它看起来更像是一个混凝土工厂。
  • @Scheff 我也想过用它来代替虚拟方法。我会选择这样做。
  • @darune 是的,也许我最好澄清一下。我将编辑问题
  • 您是否尝试过像std::vector&lt; std::variant&lt;A, B&gt; &gt; switch_type_one = {A::A1, A::A2, B::B1}; 这样非常简单的事情?也许通过一些巧妙的输入,它甚至可以扩展为用于查找构造参数的地图(尽管我不确定这会有多困难)。这种解决方案会有什么问题?

标签: c++ templates enums factory template-meta-programming


【解决方案1】:

这样的东西可能适合您的需要(使用 void* 不必引入基类,将调整留给您...):

template <typename T, typename SelType, SelType SelValue, typename ... Arguments>
class Creator
{
    std::tuple<Arguments...> arguments;

    template<size_t ... Indices>
    void* create(std::index_sequence<Indices...>)
    {
        return new T( (std::get<Indices>(arguments), ...) );
    }

public:
    Creator(Arguments ... arguments)
        : arguments(arguments...)
    { }

    using SelectorType = SelType;
    static SelType const selectorValue = SelValue;
    void* create()
    {
        return create(std::index_sequence_for<Arguments...>{});
    }
};

template<typename ... Creators>
class Switcher
{
    std::tuple<Creators ...> creators;

    template<typename T, size_t Index>//, typename First, typename ... Remaining>
    void* select(T selector)
    {
        if constexpr(Index < sizeof...(Creators))
        {
            if constexpr(std::is_same<T, typename std::tuple_element<Index, decltype(creators)>::type::SelectorType>::value)
            {
                if(selector == std::tuple_element<Index, decltype(creators)>::type::selectorValue)
                    return std::get<Index>(creators).create();
            }
            return select<T, Index + 1>(selector);
        }
        return nullptr;
    }

public:
    Switcher(Creators ... creators)
            : creators(creators...)
    { }

    template <typename T>
    void* create(T t)
    {
        return select<T, 0U>(t);
    }
};

用法:

Switcher switcher
(
        Creator<std::string, A, A::A1, char const*>("hello"),
        Creator<double, A, A::A2, double>(10.12),
        Creator<uint32_t, B, B::B1, unsigned int>(7U)
);

// just for demonstration purposes: don't care for the memory leaks...
std::cout << *(std::string*)switcher.create(A::A1) << std::endl;
std::cout << *(double*)switcher.create(A::A2) << std::endl;
std::cout << *(uint32_t*)switcher.create(B::B1) << std::endl;

在我的机器上,打印愉快:

你好 10.12 7

【讨论】:

  • 是的,这似乎是一个很酷的解决方案。我一直在寻找 C++11 中的东西,但至少这是一个可行的想法 :)
  • 我刚刚注意到这个解决方案并不完全符合我的要求。基本上我需要类似 Switcher switcher(myInputs);切换器(A::A1);切换器(A::A2);所以,只有一个适用于所有枚举的声明
  • @svoltron 它适用于 any 任意数量的枚举 - 请注意演示中包含的 B::B1;无论您想使用哪个枚举,都通过您作为模板参数提供给切换器的创建者实例进行配置...
  • @svoltron 我认为索引序列是我使用的唯一 C++14 功能。你可以自己重建它,有不止一个答案,例如。 G。 this oneSimilar,详细介绍一下。
  • @svoltron 如果您不喜欢if constexpr,请创建一个普通的if from。我使用它主要是为了确保 if 在编译时被解析 - 但由于表达式是常量,即使没有constexpr... 编译器也会这样做
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多