【问题标题】:How to implement a factory with multiple switches?如何实现具有多个交换机的工厂?
【发布时间】:2012-09-24 13:40:09
【问题描述】:

我想实现一个工厂函数来创建对象。我的对象模板如下所示:

template <typename TA, typename TB>
struct MyImpl : public MyInterface
{
    // content
};

而我的工厂是这样的:

MyInterface* factory(char ta, char tb)
{
    if(ta == 'c' && tb == 'c')
    {
        return new MyImpl<char, char>();
    }
    if(ta == 'c' && tb == 's')
    {
        return new MyImpl<char, short>();
    }
    if(ta == 's' && tb == 'c')
    {
        return new MyImpl<short, char>();
    }
    // and so on ....
}

工厂函数必须接受非静态字符数据(tatb),因为它无法在编译时确定,我认为这就是工厂的全部意义所在。事实上,tatb 是从文件(或网络)中读取的。

我想要一个更简单的解决方案来避免烦人的 2 级切换。

我认为我的问题类似于how-would-one-write-a-meta-if-else-if-in-c,除了我不能使用静态参数。

也许我应该回退到 C 宏并使用一些宏技巧来缩减我当前的代码?

提前致谢!

更新

@Rob 的回答:

我的实际代码会更复杂,其中包含许多其他内容,更难阅读,并且在许多方面都不相关。我正在尝试正确的伪代码,如果有任何问题,请告诉我:-)。

回复@Dynguss:

我的问题是,在我的实际实现中,工厂的参数(ta,tb)的范围会很大,比如10 X ta和20 X tb,而ta和tb的组合会很长,并且难以维护。所以我至少需要一些方法来简化组合工作。

【问题讨论】:

  • 工厂?你的意思是像古希腊那样的“假特洛伊”?你想写木马吗?
  • @Dynguss:我认为这个例子不言自明:&lt;short, char&gt;
  • 生成代码是一种选择吗?您可以生成一个不错的开关,并将 O(n^2) 减少到 O(n) 查找。
  • @Dynguss:它是一个工厂,根据保存的文本配置创建对象。如果不清楚,您应该检查工厂设计模式是什么。
  • 您的代码中有两个可能与问题无关的错误,也可能与问题无关。您能否发布演示您的问题的 actual (不是伪代码)?请参阅sscce.org 了解更多信息。

标签: c++ templates factory


【解决方案1】:

这是一个想法:

template <typename T>
MyInterface * factroy(char t)
{
    if (t == 'c') { return MyImpl<T, char>();  }
    if (t == 's') { return MyImpl<T, short>(); }
    // ...
}

MyInterface * factory(char ta, char tb)
{
    if (ta == 'c') { return factroy<char>(tb);  }
    if (ta == 's') { return factroy<short>(tb); }
    // ...
}

使用可变参数模板,这种模式可以扩展到任意数量的类型参数——例如:

struct Base { virtual ~Base() = default; };
template <typename A, typename B, typename C> struct Foo : Base { };

#include <tuple>

template <typename ...Args>
constexpr Base * factory(std::tuple<Args...>)
{
    return new Foo<Args...>;
}

template <typename ...Args, typename ...Char>
constexpr Base * factory(std::tuple<Args...>, char t, Char ... ts)
{
    return t == 'c' ? make(std::tuple<char,      Args...>(), ts...)
         : t == 's' ? make(std::tuple<short int, Args...>(), ts...)
         : t == 'i' ? make(std::tuple<int,       Args...>(), ts...)
         : t == 'l' ? make(std::tuple<long int,  Args...>(), ts...)
         : nullptr;
}

用法:auto p = factory(std::tuple&lt;&gt;(), 'c', 's', 'l');

【讨论】:

  • 更优雅一点,不过还是两个开关。
  • 你能示范一下吗?或者把我带到我能看到的地方?可悲的是,我目前对 11 的熟悉程度不足......
  • @Arkadiy:类似于template &lt;typename ...Args, typename ...Chars&gt; factory(Chars ... ts),然后打开。让我看看能不能装点东西。
  • @Arkadiy:我添加了一个带有可变参数模板的示例。这有点粗略,但它应该给你一个想法。
【解决方案2】:

如果您可以将 Impl 构造函数分离为 new Impl(new InnerImpl),那么可能会有一个非常复杂的解决方案,用地图替换开关:

  struct Creator {
     virtual ~Creator(){};
     virtual Interface *create(InterfaceInner *) = 0;
     virtual InterfaceInner *createInner() = 0;
  }

  std::Map<char, Creator *> creatorMap;

  template<char T>
  struct Factory {
     Factory() {
       creatorMap.insert(T, &this->creator);
    }
  }


  template<>
  struct Factory<'s'> {
    struct ShortCreator : public Creator {
     virtual Interface *create(InterfaceInner *inner) {return new Impl<short>(inner);}
     virtual InterfaceInner *createInner(return new ImplInner<short>());
    } creator;
  }




  Factory<'s'> shortFactory;
  Factory<'c'> charFactory;

  creatorMap[ta].create(creatorMap[tb].createInner());

【讨论】:

    【解决方案3】:

    怎么样:

    MyInterface* factory(char ta, char tb)
    {
        switch( ta << 8 | tb )
        {
        case 'cc': return MyImpl<char, char>();
        case 'cs': return MyImpl<char, short>();
        case 'sc': return MyImpl<short, char>();
        // and so on ....
        }
    }
    

    注意:这在 Intel/AMD x86 和 x64 上运行良好,在具有不同字节序(例如 PPC)的 CPU 上,您必须像这样交换 tatbswitch( ta | tb &lt;&lt; 8 )

    【讨论】:

    • 我明白你的意思,但我认为你的 impl 和我的伪代码之间没有什么区别,除了效率方面。可能你没有明白我的问题,还是谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多