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 ?
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.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 usingbind
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.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):