C++0x :在 std::map 中存储任何类型的 std::function
我试图在地图中存储一组 std::function (在 GCC 4.5 下)
我想得到两种东西:
- 存储已传递参数的函数;那么你就拥有了 调用 f()
- 存储不带参数的函数;那么你必须打电话 f(...)
我想我用类 Command 和 Manager 实现了第一个:
class Command
{
std::function<void()> f_;
public:
Command() {}
Command(std::function<void()> f) : f_(f) {}
void execute() { if(f_) f_(); }
};
class CommandManager
{
typedef map<string, Command*> FMap;
public :
void add(string name, Command* cmd)
{
fmap1.insert(pair<string, Command*>(name, cmd));
}
void execute(string name)
{
FMap::const_iterator it = fmap1.find(name);
if(it != fmap1.end())
{
Command* c = it->second;
c->execute();
}
}
private :
FMap fmap1;
};
可以像这样使用:
class Print{
public:
void print1(string s, string s1){ cout<<"print1 : "<<"s : "<<s<<" s1 : "<<s1<<endl; }
int print2(){ cout<<"print2"<<endl; return 2;}
};
#include <string>
#include <functional>
int main()
{
Print p = Print();
function<void()> f1(bind(&Print::print1, &p, string("test1"), string("test2")));
function<int()> f2(bind(&Print::print2, &p));
CommandManager cmdMgr = CommandManager();
cmdMgr.add("print1", new Command(f1));
cmdMgr.execute("print1");
cmdMgr.add("print2", new Command(f2));
cmdMgr.execute("print2");
return 0;
}
现在我希望能够做到这一点:
int main()
{
Print p = Print();
function<void(string, string)> f1(bind(&Print::print1, &p, placeholders::_1, placeholders::_2));
CommandManager cmdMgr = CommandManager();
cmdMgr.add("print1", new Command(f1));
cmdMgr.execute("print1", string("test1"), string("test2"));
return 0;
}
有没有办法,例如使用类型擦除?
I'm trying to store a set of std::function in a map (under GCC 4.5)
I'd like to get 2 kind of things :
- storing functions with arguments already passed; then you just have
to call f() - storing functions without arguments; then you have to call
f(...)
I think I achieved the first one with a class Command and a Manager :
class Command
{
std::function<void()> f_;
public:
Command() {}
Command(std::function<void()> f) : f_(f) {}
void execute() { if(f_) f_(); }
};
class CommandManager
{
typedef map<string, Command*> FMap;
public :
void add(string name, Command* cmd)
{
fmap1.insert(pair<string, Command*>(name, cmd));
}
void execute(string name)
{
FMap::const_iterator it = fmap1.find(name);
if(it != fmap1.end())
{
Command* c = it->second;
c->execute();
}
}
private :
FMap fmap1;
};
can be used like this :
class Print{
public:
void print1(string s, string s1){ cout<<"print1 : "<<"s : "<<s<<" s1 : "<<s1<<endl; }
int print2(){ cout<<"print2"<<endl; return 2;}
};
#include <string>
#include <functional>
int main()
{
Print p = Print();
function<void()> f1(bind(&Print::print1, &p, string("test1"), string("test2")));
function<int()> f2(bind(&Print::print2, &p));
CommandManager cmdMgr = CommandManager();
cmdMgr.add("print1", new Command(f1));
cmdMgr.execute("print1");
cmdMgr.add("print2", new Command(f2));
cmdMgr.execute("print2");
return 0;
}
Now I'd like to be able to do this :
int main()
{
Print p = Print();
function<void(string, string)> f1(bind(&Print::print1, &p, placeholders::_1, placeholders::_2));
CommandManager cmdMgr = CommandManager();
cmdMgr.add("print1", new Command(f1));
cmdMgr.execute("print1", string("test1"), string("test2"));
return 0;
}
Is there a way, using type-erasure for example ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以使用动态转换来在运行时确定列表中函数的类型。
请注意,我添加了shared_ptr来消除原始示例中的内存泄漏。如果使用错误的参数调用execute方法(如果dynamic_cast产生0),也许您想抛出异常。
用法:
代码(带可变参数模板支持,例如 gcc-4.5):
不带可变参数模板支持(例如 VS2010):
You could use dynamic cast to determine the type of the function in the list at runtime.
Please note that I added shared_ptr to remove the memory leak in the original sample. Perhaps you want to throw a exception if the execute method is called with the wrong arguments (if the dynamic_cast yields 0).
Usage:
Code (with variadic template support for example gcc-4.5):
without variadic template support (example VS2010):
如果没有一些认真的运行时工作和相关成本,您想要做的事情是不可能的。最简单的解决方案当然是只存储 boost::any< /a> (
any_function
never made it into boost) 在你的地图中并进行必要的转换(或添加一些运行时数据来告诉你要进行哪个转换),尽管你应该不惜一切代价避免这种情况,并且使用固定参数或不使用参数。然后,您的用户可以使用
bind
修改其函数以匹配您所需的签名。编辑:在您当前的方案中,我认为
CommandManager
没有理由在地图中存储Command*
。Edit2:您还可以删除返回类型。这对于您的用例来说可能没问题,但使其不那么通用。
Edit3:我使用
any
编写了一些代码的工作示例。我觉得存在一些缺陷,我真的不知道这应该实现什么,但这里是:至于使用固定签名的示例。想想你要存储的函数的最自然表示是什么(看看你的 Command 示例,我假设它是 std::function; 存储这种类型的函数,每当您的用户尝试使用它时,他都必须
绑定
他想要使用的任何函数,以便它与此签名匹配。What you are trying to do is not possible without some serious runtime work and the associated cost. The simplest solution would of course to just store a boost::any (
any_function
never made it into boost) inside your map and do the necessary casts (or add some runtime data that tells you which cast to make), although you should avoid that at any cost and go with fixed arguments or no arguments.Your users can then modify their functions using
bind
to match the signature you require.Edit: In your current scheme I see no reason for
CommandManager
to storeCommand*
in the map.Edit2: Also you drop the return type. This could be OK for your use-case but makes this a lot less generic.
Edit3: I worked out some working example of your code using
any
. I feel that there is some flaw and I really don't see what this should achieve but here it goes:As for the example using a fixed signature. Just think of what would be the most natural representation of a function you are going to store (looking at your
Command
example I'd assume it isstd::function<void(void)>
. Store functions of this type and whenever one your users tries to use it, he has tobind
whatever function he wants to use, so it matches this signature.您的
Command
类构造函数需要一个function
。您正在尝试为其提供一个function
。这不会进行类型检查。如果您需要接受可变参数的函数(例如
printf
),则需要接受可变参数的function<>
和execute()
。您需要知道如何使用它(特别是,您需要一个固定的第一个参数)。然后,您负责类型安全,就像printf
一样。如果您只需要可变数量的字符串参数,请使用接受字符串向量等的函数。
所有这些与
std::map
无关。无论您可以存储在普通旧变量中,您也可以存储在 std::map 中。Your
Command
class constructor needs afunction<void()>
. You are trying to feed it afunction<void(string,string)>
. This is not going to typecheck.If you need functions that accept variable arguments (like
printf
), you will needfunction<>
andexecute()
that accept variable arguments. You need to know how to work with that (in particular, you need a fixed first argument). You are then responsible for type safety, much like withprintf
.If you just need a variable number of string arguments, use functions that accept e.g. vectors of strings.
All this has nothing to do whatsoever with
std::map
. Whatever you can store in a plain old variable, you can store instd::map
too.