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

Started by
12 comments, last by NotAYakk 17 years, 11 months ago
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.
|aaap.penopticon.com| My website, including game projects. Collaboration/comments are welcome, please visit :)
Advertisement
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.
&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));
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
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
CheersChris
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?
|aaap.penopticon.com| My website, including game projects. Collaboration/comments are welcome, please visit :)
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
- John
I thought boost::function was a implementation of functor?
|aaap.penopticon.com| My website, including game projects. Collaboration/comments are welcome, please visit :)
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.
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.
|aaap.penopticon.com| My website, including game projects. Collaboration/comments are welcome, please visit :)
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.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement