Фабрика шаблонов Variadic с использованием std::bind и std::placeholder

Я пытаюсь реализовать шаблон Factory, который может создавать экземпляры подключаемых модулей в экземплярах подключаемых модулей. Цель состоит в том, чтобы иметь один класс Factory, определяемый типом плагина, и аргументы функции create плагина, например:

APluginInstance и A2PluginInstance все получают ABase. Каждый из них должен иметь свой плагин на фабрике, но на фабрике должен быть сопоставлен с именем.

class ABase {};

class APluginInstance : public ABase {

public:

    static std::shared_ptr<ABase> create(double a, double b)
    {
        return std::shared_ptr<ABase>(new APluginInstance(a,b));
    }

    APluginInstance(double a, double b)
    : ret(a + b)
    {
        std::cout << ret << std::endl;
    }

    double ret;
};


class A2PluginInstance : public ABase {

public:

    static std::shared_ptr<ABase> create(double a, double b)
    {
        return std::shared_ptr<ABase>(new APluginInstance(a,b));
    }

    A2PluginInstance(double a, double b)
    : ret(a - b)
    {
        std::cout << ret << std::endl;
    }

    double ret;
};

Точно так же у нас есть другой тип плагина где-то еще:

class BBase {};

class BPluginInstance : public BBase {

public:

    static std::shared_ptr<BBase> create(int a)
    {
        return std::shared_ptr<BBase>(new BPluginInstance(a));
    }

    BPluginInstance(int a)
    {
        std::cout  << a << std::endl;
    }
};

class B2PluginInstance : public BBase {

public:

    static std::shared_ptr<BBase> create(int a)
    {
        return std::shared_ptr<BBase>(new B2PluginInstance(a));
    }

    B2PluginInstance(int a)
    {
        std::cout  << -a << std::endl;
    }
};

Теперь вот общий класс Plugin, который принимает в параметрах шаблона базовый тип экземпляра (тот, который был возвращен из функции create) и аргументы этой функции:

template <typename PluginInstanceBaseType, typename ...Args>
class Plugin
{
public:

    using PluginInstanceBase_t = PluginInstanceBaseType;


    void setFunc(const std::function<PluginInstanceBaseType(Args...)>& func)
    {
        _createFunc = func;
    }

    PluginInstanceBaseType create(const Args&... args)
    {
        return _createFunc(args...);
    }

    std::function<PluginInstanceBaseType(Args...)> _createFunc;

};

Итак, у нас есть 2 типа плагинов:

using APlugin = Plugin<std::shared_ptr<ABase>, double, double>;
using BPlugin = Plugin<std::shared_ptr<BBase>, int>;

Теперь вот фабричный класс, который принимает тип плагина в параметре (APlugin или BPlugin)

template <typename PluginType>
class Factory
{

public:

    using PluginInstanceBase_t = typename PluginType::PluginInstanceBase_t;


    template <typename T, typename...Args>
    void addPlugin(const std::string& name, const std::function<T(Args...)>& f)
    {
        std::shared_ptr<PluginType> plugin(new PluginType());
        plugin->setFunc(f);
        _plugins[name] = plugin;
    }

    template <typename... Args>
    PluginInstanceBase_t create(const std::string& name, const Args&... args)
    {
        auto found = _plugins.find(name);
        if (found == _plugins.end()) {
            return PluginInstanceBase_t();
        }
        return found->second->create(args...);
    }

private:


    std::map<std::string, std::shared_ptr<PluginType> > _plugins;
};

Наши 2 фабрики:

using AFactory = Factory<APlugin>;
using BFactory = Factory<BPlugin>;

Наконец, вот пример использования:

int main()
{

     {
        // Works
        APlugin tmpa;
        tmpa.setFunc(APluginInstance::create);
    }

    AFactory af;
    BFactory bf;

    // Doesn't work
    //af.addPlugin("A1", APluginInstance::create);
    //af.addPlugin("A2", A2PluginInstance::create);
    //bf.addPlugin("B1", BPluginInstance::create);
    //bf.addPlugin("B2", B2PluginInstance::create);

    std::shared_ptr<ABase> a = af.create("A1",2., 4.);
    std::shared_ptr<ABase> a2 = af.create("A2",2., 4.);
    std::shared_ptr<BBase> b = bf.create("B1", 1);
    std::shared_ptr<BBase> b2 = bf.create("B2", 1);

    return 0;
}

Создание плагина и вызов setFunc с помощью функции create instance работает напрямую. Однако при использовании функции addPlugin из фабрики она не работает, поскольку не может сопоставить функцию create с типом std::function.

Я пытаюсь найти решение для этого, чтобы я мог использовать функцию addPlugin

Вот ссылка на Coliru для живого примера:

http://coliru.stacked-crooked.com/a/cba8fe84b4d6bd85

1 ответ

Решение

Ваша проблема в том, что вы передаете указатель функции, а не std::function для addPlugin.

Вы запрашиваете преобразование имплика из вашего указателя T(*)(Args...) к std::function<T,Args...>, то вычет за T,Args... иметь место. Но неявные преобразования не учитываются для вывода аргументов шаблона.

Вам либо нужно пройти std::function в addPlugin, так T,Args... может быть выведен, или вам нужна другая перегрузка addPlugin который принимает указатель на функцию, а затем вызывает перегрузку std::function addPlugin, Что-то вроде..

...

template <typename T, typename...Args>
void addPlugin(const std::string& name, T(*func)(Args...) )
{
    addPlugin(name,std::function<T(Args...)>(func));
}

template <typename T, typename...Args>
void addPlugin(const std::string& name, const std::function<T(Args...)>& f)
{
    std::shared_ptr<PluginType> plugin(new PluginType());
    plugin->setFunc(f);
    _plugins[name] = plugin;
}

...

демонстрация

Другие вопросы по тегам