A map of functions with variadic signatures in c++

跟風遠走 提交于 2019-12-08 03:20:58

问题


From

Martin Reddy's API Design for C++ - Chapter 3 (section 3.3.3 Extensible Factory Example)

I found this implementation of Factory pattern to be pretty efficient which allows a user to register callback functions (essentially constructors for the derived classes) at run time, which can eventually be called when creating an object of that type. The code is shown below, as taken from the textbook -

File : rendererfactory.h

class RendererFactory
{
public:
    typedef IRenderer *(*CreateCallback)();
    static void RegisterRenderer(const std::string &type, CreateCallback cb);
    static void UnregisterRenderer(const std::string &type);
    static IRenderer *CreateRenderer(const std::string &type);
private:
    typedef std::map<std::string, CreateCallback> CallbackMap;
    static CallbackMap mRenderers;
};

File : rendererfactory.cpp

#include "rendererfactory.h"

// instantiate the static variable in RendererFactory
RendererFactory::CallbackMap RendererFactory::mRenderers;

void RendererFactory::RegisterRenderer(const std::string &type, CreateCallback cb)
{
    mRenderers[type] = cb;
}

void RendererFactory::UnregisterRenderer(const std::string &type)
{
    mRenderers.erase(type);
}

IRenderer *RendererFactory::CreateRenderer(const std::string &type)
{
    CallbackMap::iterator it = mRenderers.find(type);
    if (it != mRenderers.end())
    {
        // call the creation callback to construct this derived type
        return (it->second)();
    }
    return NULL;
}

class UserRenderer : public IRenderer
{
public:
    ~UserRenderer() {}
    static IRenderer *Create() { return new UserRenderer(); }
};

File : main.cpp

int main(int, char **)
{
    // register a new renderer
    RendererFactory::RegisterRenderer("user", UserRenderer::Create);

    // create an instance of our new renderer
    IRenderer *r = RendererFactory::CreateRenderer("user");

    r->Render();

    delete r;

    return 0;
}

My limitation with this code is that it assumes are constructors of derived objects, do not take any arguments. For instance if I had a derived class -

class UserRendererMultiArgs : public IRenderer
{
public:
    UserRendererMultiArgs(int, int);
    ~UserRendererMultiArgs() {}
    static IRenderer *Create() { 
        return new UserRendererMultiArgs(); //Incorrect : need to call UserRendererMultiArgs(int, int) ??? 
    }
};

How would I go about achieving the same results of registering callback with variable arguments in the map maintained by the RendererFactory class?

I have though of using varargs but I am not sure how to do it ?!


回答1:


Ignoring the factory pattern and going with the title of the question then this might do what you want:

#include <map>
#include <memory>
#include <string>

struct IRenderer {};

class UserRendererMultiArgs : public IRenderer {
    public:
    UserRendererMultiArgs(int, int) {}
    ~UserRendererMultiArgs() {}
    static IRenderer *Create(int i1, int i2) {
        return new UserRendererMultiArgs(i1, i2);
    }
};

template <class... Args>
struct MapHolder{
    static std::map<std::string, IRenderer *(*)(Args...)> CallbackMap;
};

template <class... Args>
std::map<std::string, IRenderer *(*)(Args...)> MapHolder<Args...>::CallbackMap;

class RendererFactory {
    public:
    template <class... Args>
    static void RegisterRenderer(std::string name, IRenderer *(*Callback)(Args...)) {
        MapHolder<Args...>::CallbackMap[name] = Callback;
    }

    template <class... Args>
    static IRenderer *Create(const std::string &name, Args &&... args) {
        return MapHolder<Args...>::CallbackMap[name](std::forward<Args>(args)...);
    }
};

int main() {
    RendererFactory::RegisterRenderer("user", &UserRendererMultiArgs::Create);
    std::unique_ptr<IRenderer> r{RendererFactory::Create("user", 42, 3)};
}

(demo to play with)

In C++14 you have variable templates to not need that MapHolder, but the tag specified C++11.




回答2:


If you know the arguments at compile time you can use

RendererFactory::RegisterRenderer("multiuser", []{
    return new UserRendererMultiArgs(1, 2);
});

If you do not know the arguments at compile time you can use

int i = 0, j = 0;
std::cin >> i >> j;
RendererFactory::RegisterRenderer("multiuser", [i, j]{
    return new UserRendererMultiArgs(i, j);
});

However, now that the lambda carries state it cannot be assigned to a function pointer anymore, so you would need to change the type of CreateCallback to std::function<IRenderer *()> which incurs some cost due to dynamic memory allocations required to capture arbitrarily sized function objects.



来源:https://stackoverflow.com/questions/42044150/a-map-of-functions-with-variadic-signatures-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!