C++ 中具有可变参数签名的函数映射
A map of functions with variadic signatures in c++
来自
Martin Reddy's API Design for C++ - Chapter 3 (section 3.3.3
Extensible Factory Example)
我发现这种工厂模式的实现非常有效,它允许用户在 运行 时间注册回调函数(本质上是派生的 classes 的构造函数),最终可以调用创建该类型的对象时。代码如下所示,摘自教科书-
文件: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;
};
文件: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(); }
};
文件: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;
}
我对这段代码的限制是它假定是派生对象的构造函数,不带任何参数。例如,如果我有一个派生的 class -
class UserRendererMultiArgs : public IRenderer
{
public:
UserRendererMultiArgs(int, int);
~UserRendererMultiArgs() {}
static IRenderer *Create() {
return new UserRendererMultiArgs(); //Incorrect : need to call UserRendererMultiArgs(int, int) ???
}
};
我将如何在由 RendererFactory class 维护的映射中使用可变参数注册回调来实现相同的结果?
我想过使用可变参数,但我不确定该怎么做?!
如果您在编译时知道参数,您可以使用
RendererFactory::RegisterRenderer("multiuser", []{
return new UserRendererMultiArgs(1, 2);
});
如果您不知道编译时的参数,您可以使用
int i = 0, j = 0;
std::cin >> i >> j;
RendererFactory::RegisterRenderer("multiuser", [i, j]{
return new UserRendererMultiArgs(i, j);
});
但是,既然 lambda 携带状态,它就不能再分配给函数指针,因此您需要将 CreateCallback
的类型更改为 std::function<IRenderer *()>
,这会因动态而产生一些成本捕获任意大小的函数对象所需的内存分配。
忽略工厂模式并使用问题的标题,那么这可能会做你想要的:
#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)};
}
在 C++14 中,您有变量模板不需要 MapHolder
,但标签指定了 C++11。
来自
Martin Reddy's API Design for C++ - Chapter 3 (section 3.3.3 Extensible Factory Example)
我发现这种工厂模式的实现非常有效,它允许用户在 运行 时间注册回调函数(本质上是派生的 classes 的构造函数),最终可以调用创建该类型的对象时。代码如下所示,摘自教科书-
文件: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;
};
文件: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(); }
};
文件: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;
}
我对这段代码的限制是它假定是派生对象的构造函数,不带任何参数。例如,如果我有一个派生的 class -
class UserRendererMultiArgs : public IRenderer
{
public:
UserRendererMultiArgs(int, int);
~UserRendererMultiArgs() {}
static IRenderer *Create() {
return new UserRendererMultiArgs(); //Incorrect : need to call UserRendererMultiArgs(int, int) ???
}
};
我将如何在由 RendererFactory class 维护的映射中使用可变参数注册回调来实现相同的结果?
我想过使用可变参数,但我不确定该怎么做?!
如果您在编译时知道参数,您可以使用
RendererFactory::RegisterRenderer("multiuser", []{
return new UserRendererMultiArgs(1, 2);
});
如果您不知道编译时的参数,您可以使用
int i = 0, j = 0;
std::cin >> i >> j;
RendererFactory::RegisterRenderer("multiuser", [i, j]{
return new UserRendererMultiArgs(i, j);
});
但是,既然 lambda 携带状态,它就不能再分配给函数指针,因此您需要将 CreateCallback
的类型更改为 std::function<IRenderer *()>
,这会因动态而产生一些成本捕获任意大小的函数对象所需的内存分配。
忽略工厂模式并使用问题的标题,那么这可能会做你想要的:
#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)};
}
在 C++14 中,您有变量模板不需要 MapHolder
,但标签指定了 C++11。