Sign in to follow this  
AAAP

Command Pattern (with func pointers) (C++)

Recommended Posts

AAAP    137
I posted about this in the Game Programming forum, but maybe it's more appropriate here.. and I will elaborate on the question: Using the command pattern with function pointers (instea dof writing new Execute method for command classes, use single class per reciever and point the execute method to desired function), i get decent usable functionality. However, I can only make calls to functions with no arguments (Because of "typedef void (Game::* Action)();", which is set for no arguments!!) My solution was to make another subclass of Command class, with Action typedefed with an argument.. So I had to prototype new Execute method in Command, and make it do nothing in other subclass, of course. Anyway, it seemed like this should work for me, but when I tried to run it it gives message "illegal call of non-static member function" because of this line: boost::shared_ptr<Command> cmd(new StringCommand(&game, &Game::OpenMenu(file))); StringCommand is the subclass of Command that should accept 1 std string as argument. Am I just trying to send address of function+argument to the StringCommand object wrong, or is this just impossible to work anyway? If any other code is needed, id be happy to post some.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:
Original post by AAAP
Am I just trying to send address of function+argument
Yeah, looks like it. Except that there's no such thing, so you get an error. You need to use an object which holds the argument's value and has a () operator which will call Game::OpenMenu with the argument.

But an easier way is to use a generic facility which does the same thing and you won't need to manually define a new class for each function. Since you're already using boost, may I suggest you look up boost.function and boost.bind. Together they provide exactly what you need.

Share this post


Link to post
Share on other sites
Fruny    1658
&Game::OpenMenu(file) is trying to call the supposed-static OpenMenu member function of the Game class with file as a parameter and then take the address of the result. Since the member function is non-static, the code is illegal.

It is definitely not giving you a pointer to a curried member function. C++ has no automated currying mechanism.

Since you're already using Boost, consider using boost::function and boost::bind instead of naked function pointers. You'll likely find they already do everything you want.

new StringCommand(boost::bind(&Game::OpenMenu, &game, file));

Share this post


Link to post
Share on other sites
chollida1    532
I'd second what Frunny said about boost::bind. We use it along with boost signals and slots for our command pattern architecture.

Cheers
Chris

Share this post


Link to post
Share on other sites
AAAP    137
Hey, I took your advice and started switch to just using the boost function ptrs and bind system.

I used http://www.codeproject.com/library/BoostBindFunction.asp website as a base.

Now, i see that you need to make different function templates for different varieties of variables...

so this puts me in the same situation that i was in before, essentially. theres only room for one "kind of function" in the command container. Any tips?

Share this post


Link to post
Share on other sites
Teknofreek    331
Quote:
Original post by AAAP
so this puts me in the same situation that i was in before, essentially. theres only room for one "kind of function" in the command container. Any tips?


I may be missing something, but I would prefer functors instead of function pointers. One of the main reasons is that you can de-couple the initialization from the calling. The upshot of this is that you can make all of the functor calls the same. For example they could take no arguments and return a bool indicating success...or whatever. However, since each command is now a class you can initialize it however you like. So, lets say some command needs to know where you clicked. You could pass the point into the constructor and store it off. However, the call would still be "bool Execute();".

I think this approach is simpler than trying to support an arbitrary calling mechanism. Plus, it means you can do nifty things like derive commands and such :)

-John

Share this post


Link to post
Share on other sites
NotAYakk    876

typedef void (*void_func)(void*);
typedef std::pair<void_func, void*> callback;

template<typename T>
struct round_trip {
typedef void(*T_func)(T*);

typedef std::pair<T_func, T*> Tcallback;

static void wrapper_func(void* v) {
Tcallback* t = reinterpret_cast<Tcallback*>(v);
t->first(t->second);
delete t;
}
static callback make_callback(T_func f, T* t) {
Tcallback* cb = new Tcallback( f, t );
callback retval = callback( wrapper_func, reinterpret_cast<void*>(cb);
return retval;
}
};

template<typename T>
callback make_callback(void (*f)(T*), T* t) {
return round_trip<T>::make_callback(f, t);
}


Voila. "Typesafe" callbacks that take arbitrary T*s. =)

The above is designed for "do the callback once".

If you want "do the callback more than once", you need both "do callback" and "clean up callback" callback functions.

Share this post


Link to post
Share on other sites
AAAP    137
can T be in the form of a whole function prototype such as the return type and parameters? sorry for the newb questions, but this whole topic seems a little over my head at the moment.

Share this post


Link to post
Share on other sites
Fruny    1658
Quote:
Original post by AAAP
Now, i see that you need to make different function templates for different varieties of variables... so this puts me in the same situation that i was in before, essentially. theres only room for one "kind of function" in the command container. Any tips?


I have a very simple question for you, but I want you to really think before answering:

If you somehow managed to lump together functions which expect varied number and types of arguments, how would you go about actually calling them? You would be constrained to a single interface regardless of the number and type of parameters in the function call!





The only sound mechanism I can think of is to pass around a std::vector<boost::any> or std::vector<boost::variant<stuff> > and have the client function extract the actual parameters.

I wrote a bit of a class to do that, but it's incomplete and rather messy. If you want it, you're welcome to it.

An alternative is to have a battery of operator()(T0, T1...) templates that cast the pointer to the appropriate function pointer type but *ugh* that involves some serious tightrope-walking there. Without a net.

Or you can have them all accept a void* and pray.

Share this post


Link to post
Share on other sites
Fruny    1658
Quote:
Original post by AAAP
can T be in the form of a whole function prototype such as the return type and parameters? sorry for the newb questions, but this whole topic seems a little over my head at the moment.


With VC6, no, with better compiler, yes:

boost::function<int (double, std::string)> func;

or (VC6)

boost::function2<int, double, std::string> func;

Share this post


Link to post
Share on other sites
Teknofreek    331
Quote:
Original post by AAAP
I thought boost::function was a implementation of functor?


To be honest, I've never used boost::function so I can't say for sure. However, I thought it was a way to wrap function pointers, pointers to member functions, or functors in such a manner that you could use them interchangeably.

Either way, if you want to use different inputs to different functions, using functors seems the sanest and simplest way to me. Just initialize or update each command object any way you want. Then, when you iterate through and call them just call Execute() or whatever.

FWIW, I've used this approach before in situations like this and it's worked great for me!

-John

Share this post


Link to post
Share on other sites
AAAP    137
Well, I'm just settling for making everything use the same parameters (for commands at least), I guess it's not a big deal.

Share this post


Link to post
Share on other sites
NotAYakk    876
Quote:
Original post by AAAP
can T be in the form of a whole function prototype such as the return type and parameters? sorry for the newb questions, but this whole topic seems a little over my head at the moment.


In my case, no.

When you are dealing with "callbacks", it is often the case that you have the callback pull it's own data from where it needs it, instead of being provided with the data.

If you think about it, if you had a list of functions with two different signatures, how would you know how to call the functions?

Instead, you provide the callback with "too much information", and the callback filters out what it cares about.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this