【问题标题】:C++ Factory using variadic template issues使用可变参数模板问题的 C++ 工厂
【发布时间】:2015-08-13 15:29:46
【问题描述】:

我只是在尝试一些东西 - 我想创建一个通用工厂,它将 shared_ptr 返回到一个类型。

我有一个派生类,它使用静态方法将shared_ptr 返回给基类。这个想法是我希望能够使用通用工厂注册这些方法,但它无法确定在编译时注册哪个方法。也许有一种方法可以使用 SFINAE 实现这一点,但我才刚刚开始了解它的复杂性。

对相当长的代码示例表示歉意,也可以在 http://coliru.stacked-crooked.com/a/331e08de86004592 获得

在“DerivedA”中启用多个工厂方法将导致编译错误。

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>

// Factory which returns a shared_ptr of type T.
template<class T, class Tag, class... Args>
class NameFactory
{
public:
    typedef std::function<std::shared_ptr<T>(Args...)> Function;

    static NameFactory& instance();

    void registerType(const std::string& type, const Function& createFunction);
    std::shared_ptr<T> createObject(const std::string& type, Args&&... arguments);
private:
    NameFactory() {}

    std::unordered_map<std::string, Function> m_functionMap;
};

template<class T, class Tag, class... Args>
NameFactory<T, Tag, Args...>& NameFactory<T, Tag, Args...>::instance()
{
    static NameFactory<T, Tag, Args...> m_instance;
    return m_instance;
}

template<class T, class Tag, class... Args>
void NameFactory<T, Tag, Args...>::registerType(const std::string& type, const Function& createFunction)
{
    m_functionMap[type] = createFunction;
}

template<class T, class Tag, class... Args>
std::shared_ptr<T> NameFactory<T, Tag, Args...>::createObject(const std::string& type, Args&&... arguments)
{
    auto iter(m_functionMap.find(type));

    if (iter != m_functionMap.end())
    {
        return (iter->second)(std::forward<Args>(arguments)...);
    }

    throw std::logic_error("Cannot find constructor for type '" + type + "'");
}

template<class T, class Tag, class... Args>
class NameFactoryRegistration
{
public:
    typedef NameFactory<T, Tag, Args...> Factory;
    NameFactoryRegistration(const std::string& type, const typename Factory::Function& createFunction)
    {
        Factory::instance().registerType(type, createFunction);
    }
private:
};

class MyBase
{
public:
    typedef std::shared_ptr<MyBase> SPtr;
};

class DerivedA : public MyBase
{
public:
    static SPtr create()
    {
        return SPtr(new DerivedA);
    }

    // Enabling this factory method (and/or the two args method below causes an 'unresolved overloaded function type' error
    //static SPtr create(const std::string& s)
    //{
    //    return SPtr(new DerivedA(s));
    //}

    //static SPtr create(const std::string& s, double d)
    //{
    //    return SPtr(new DerivedA(s,d));
    //}
private:
    DerivedA()
    {
        std::cout << "DerivedA - no args" << std::endl;
    }

    DerivedA(const std::string& s)
    {
        std::cout << "DerivedA - one arg: " << s << std::endl;
    }

    DerivedA(const std::string& s, double d)
    {
        std::cout << "DerivedA - two args: " << s << " : " << d << std::endl;
    }
};

// Tags to help differentiate the factories
struct NoArgsReg;
struct SingleArgReg;
struct TwoArgReg;

typedef NameFactory<MyBase, NoArgsReg> NoArgsFactory;
typedef NameFactoryRegistration<MyBase, NoArgsReg> NoArgsRegistration;

typedef NameFactory<MyBase, SingleArgReg, const std::string&> SingleArgFactory;
typedef NameFactoryRegistration<MyBase, SingleArgReg, const std::string&> SingleArgRegistration;

typedef NameFactory<MyBase, TwoArgReg, const std::string&, double> TwoArgsFactory;
typedef NameFactoryRegistration<MyBase, TwoArgReg, const std::string&, double> TwoArgsRegistration;

// Register the factory methods into the NameFactory
NoArgsRegistration dAReg0("A", DerivedA::create);
//SingleArgRegistration dAReg1("A", DerivedA::create);
//TwoArgsRegistration dAReg2("A", DerivedA::create);


int main()
{
    auto object0(NoArgsFactory::instance().createObject("A"));

    // Not registered, 
    //auto object1(SingleArgFactory::instance().createObject("A","testString"));
    //auto object2(TwoArgsFactory::instance().createObject("A","testString",3.142));

    return 0;
}

【问题讨论】:

    标签: c++ templates variadic-templates sfinae


    【解决方案1】:

    问题在于(在 C++14 之前)std::function&lt;R(A...)&gt; 可以从 任何东西 构造,而不仅仅是从支持 R(A...) 调用的东西构造。如果您添加 registerType 的重载,它应该会有所帮助,这将采用 R (&amp;)(Args&amp;&amp;...) 参数。

    【讨论】:

    • 我认为为 std::function 添加 SFINAE 不会有帮助。
    • 感谢 Angew - 正如我在下面提到的,我没有考虑编译器将如何解释函数指针与 std::function,并认为 std::function 可以作为替代品。
    【解决方案2】:

    问题是您无法推断出重载集中的类型。即使我们将示例简化为我们可以尝试使用 SFINAE 的东西,我们也会陷入困境:

    #include <functional>
    
    struct A {
        static void create() { }
        static void create(int ) { }
    };
    
    template <typename F,
              typename = decltype(std::declval<F>()(std::declval<int>()))>
    void foo(F ) { }
    
    int main() {
        foo(&A::create); // error, even in this case
    }
    

    您必须为函数指针添加显式重载来处理这种情况,因为标准中有一个例外允许这样做:

    void foo(void (*)(int)) { } // (1)
    
    template <typename F,
              typename = decltype(std::declval<F>()(std::declval<int>()))>
    void foo(F ) { }            // (2)
    
    int main() {
        foo(&A::create); // OK, calls (1)
    }
    

    在您的具体示例中,这意味着添加两个构造函数:

    // in Factory
    using Function = std::function<std::shared_ptr<T>(Args...)>;
    using FunctionPtr = std::shared_ptr<T>(*)(Args...);
    
    // in Registration
    using Function = typename Factory::Function;
    using FunctionPtr = typename Factory::FunctionPtr;
    
    NameFactoryRegistration(const std::string& type, const Function& createFunction) {
        /* same as before */
    }
    
    NameFactoryRegistration(const std::string& type, FunctionPtr createFunction)
    : NameFactoryRegistration(type, Function(createFunction))
    { }
    

    【讨论】:

    • 太棒了,谢谢巴里 - 知道非常有用!我没有考虑过使用函数指针,我认为 std::function 是可以完全取代它们的“灵丹妙药”。
    猜你喜欢
    • 1970-01-01
    • 2014-02-19
    • 1970-01-01
    • 2018-11-26
    • 2011-09-28
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多