C ++ Factory使用可变参数模板问题

时间:2015-08-13 15:29:47

标签: c++ templates variadic-templates sfinae

我只想尝试一下 - 我想创建一个通用工厂,它会将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;
}

2 个答案:

答案 0 :(得分:3)

问题是(在C ++ 14之前)std::function<R(A...)>可以从任何东西构建,不仅仅来自支持R(A...)调用的东西。如果您添加registerType的重载,它将采用R (&)(Args&&...)参数。

答案 1 :(得分: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))
{ }