Skip to main content
GameDev.net gamedev.net
🔒 Locked

Generic function pointer container...

Started by xylent Mar 1, 2009 at 1:30 PM 12 replies 6.1k views
Original Post
xylent
xylent
Hello, I have encountered this problem with function pointers while implementing a component for my game engine, but I post it in this section of the forums since it is rather a general programming problem. I want to implement a Quake-like console along with a scripting engine. I'd like to do this mainly to make testing and debugging easier. There are many problems to solve like how to draw the console on the screen, how to receive the input, how to parse commands etc. However, these are not my concerns at the moment as I have implemented similar things many times before. My problem is that I want to implement a generic function pointer container to store the function pointers that correspond to console commands, i.e. the pointer to the function to execute when a particular command (and possibly zero or more parameters) are sent to the command executer from the console. The real problem here is that I don't want to restrict the signature of these functions to some predefined signature and the other problem is that I want to be able to use member functions with these systems since the functions I'll be calling will mostly be member functions. I have read several articles about using member function pointers conveniently (and fast) but I'm not sure how I can store functions with different signatures in the same container. One other important thing is that I have a working system now and I'd like to change other classes in my game engine as little as possible (preferably no changes at all), e.g. changes like defining a new base class and having other classes subclass this base class etc. I'd like to listen to your suggestions/opinions about the issue I mentioned above. If my problem is not clear enough, which is likely, please let me know and I'll try to explain it more clearly and possibly along with some code and usage examples. Thanks for your time,
NumberXaero
NumberXaero
For my console system I implemented this functionality using delegates. The end result is any function, global or member, can be called from the same place. The only draw back of this setup is expanding the number of arguments requires the template class it uses needs to be redefined to accept more args, and the calling code may call members of deleted objects if the object does not unregister its call when it is deleted. Other then that it is extremely flexible because it can call anything anywhere. Its basic idea is from this article

Delegate

although i dont use operator overloaded as the article has, instead i use Register/Unregister calls



[Edited by - NumberXaero on March 1, 2009 2:15:55 PM]
MaulingMonkey
MaulingMonkey
The boost library has boost::function for storing function pointers in. However, you'll still need to restrict the arguments to some specific type -- C++ has no magic autodispatch that can take N arguments and pass them to a function at runtime.
dascandy
dascandy
Quote:
Original post by MaulingMonkey
The boost library has boost::function for storing function pointers in. However, you'll still need to restrict the arguments to some specific type -- C++ has no magic autodispatch that can take N arguments and pass them to a function at runtime.


That library will become part of the standard C++ library (STL) later this year or early next year. Just FYI.
xylent
xylent
Thank you for your replies, I have read both the Delegate tutorial and the boost::function documentation before posting here though. They don't quite solve my problem, if I understand the documentation correct and completely.

What I really want to do is, let there be two functions such as:

void setRenderMode(int renderMode);
bool terminateApplication();

What I'm trying to do is to store function pointers to both of the above functions in the same container. I know it is hard to do this in standard c++ in a convenient way. But I can do a similar thing with data pointers. For example:

int i;
float f;

Then I can store pointers to both of these variables as void* in an STL container along with an enum value indicating their type. Then when I need to assign a given value to any of them I can check the enum value and decide to do something as follows:

*(float *)void_ptr_to_f = new_value;

I thought there would be a way to do this with the function pointers as well. My main concern is to store them in the same container. How to call them is another issue and I know that c++ won't magically format/order/choose parameters correctly for me, pass it to the function and invoke it.

dascandy
dascandy
Quote:
Original post by xylent
Thank you for your replies, I have read both the Delegate tutorial and the boost::function documentation before posting here though. They don't quite solve my problem, if I understand the documentation correct and completely.

What I really want to do is, let there be two functions such as:

void setRenderMode(int renderMode);
bool terminateApplication();

What I'm trying to do is to store function pointers to both of the above functions in the same container. I know it is hard to do this in standard c++ in a convenient way. But I can do a similar thing with data pointers. For example:

int i;
float f;

Then I can store pointers to both of these variables as void* in an STL container along with an enum value indicating their type. Then when I need to assign a given value to any of them I can check the enum value and decide to do something


Ah.

Boost::Any or Boost::Variant.

But why would you want to? Just because you can put a dog, a cat and a mouse in one box doesn't mean it makes any sense to do so. You can also use polymorphic wrappers if it's a limited group of things (and if it isn't, god help your code).
xylent
xylent
Quote:
Original post by dascandy
Ah.

Boost::Any or Boost::Variant.

But why would you want to? Just because you can put a dog, a cat and a mouse in one box doesn't mean it makes any sense to do so. You can also use polymorphic wrappers if it's a limited group of things (and if it isn't, god help your code).


There will be many console commands with corresponding calls to many different functions of many different classes throughout the system. I want to be able to add new console commands and associate them with existing functions easily. What I'm seeking for may not be possible with c++ at all, I suspect. In that case, what would be a good way to design such a system?

My main concern is that it should be really easy for me to add new console commands to the console reaching anywhere in the system. I know this may destroy encapsulation and possibly many other object-oriented principles. Although I'm trying to design the system obeying as much as possible to the OOP, without hurting performance, this will be mainly for testing and debugging as I said. So I don't really care about object-oriented design here.

CrazyCdn
CrazyCdn
std::map ConsoleCommands

string is for the commands, function stores the function to call. Done. Need to send multiple variables? Make a base class thats empty, then via inheritence make the class that holds all the varibles the function needs. Since you KNOW what your sending (or it should fail if you send something else) your golden. Extending this is overly easy. Maintaining it should be a breeze and it works nicely without being overly complicated like your going for.

class BaseVariables
{
};

class SetRenderStateVariables: public BaseVariables
{
...
}

ConsoleCommands.insert(std::makepair("SetRenderState", Class::FunctionToCall));

FunctionToCall are all the same format, all take a pointer to BaseVariable class.

Should do the trick. Sorry wrote this in a minute as I'm on my way out the door.
"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin
xylent
xylent
Thank you for your contribution, Mike2343. The design you mentioned is definitely one way of designing such a system. But, still, it means that I have to write wrapper functions for each function that I want to add to console as a command. For example, for my function:

void setRenderMode(int renderMode);

I'll need to write a wrapper function, such as:

void setRenderModeWrapper(BaseVariables *var)
{
// cast var to SetRenderModeVariables
// call setRenderMode with appropriate parameters
}

And then I can add this wrapper function to my console commands container. For every single console command I'd like to add I'll have to write a wrapper function like the one above and also most of the times I'll have to define a new class that subclasses BaseVariables class.

This is still too much of a overhead for me, but I guess what I want is simply not possible. So I'll stick with your suggestion.

Thank you all guys for your replies.

Best,
the_edd
the_edd
Quote:
Original post by xylent
There will be many console commands with corresponding calls to many different functions of many different classes throughout the system. I want to be able to add new console commands and associate them with existing functions easily.


Another approach would be to use a scripting language. Lua is a good candidate. And Luabind makes it incredibly easy to map your C++ code in to Lua. Your console is then nothing more than a Lua interpreter.

If you hand-roll a solution, you'll end up repeating a lot of the stuff that Lua/Luabind does for you e.g. marshalling types between C++ and your console's command language, defining, implementing and using a custom calling convention, and so on...

There are other choices of scripting language too. I mention Lua because I've had very nice experiences with it. Boost.Python is a similar option for Python.

nullsquared
nullsquared
#include <boost/any.hpp>#include <boost/function.hpp>#include <boost/bind.hpp>#include <map>#include <string>#include <vector>typedef std::vector<boost::any> parameters;typedef boost::function<void(const parameters&)> consoleFunc;typedef std::map<std::string, consoleFunc> consoleFuncMap;consoleFuncMap consoleFuncs;void consoleSetRenderMode(const parameters &params){    assert(params.size() == 1); // should have 1 parameter    int renderMode = boost::any_cast<int>(params[0]);    renderer->setRenderMode(renderMode);}// for example, an added bind-time parametervoid consoleTerminateApp(const parameters &params, application *app){    assert(params.empty()); // terminate should have no parameters    app->quit();}void consoleAddAndPrint(const parameters &params){    assert(params.size() == 2); // 2 numbers to add    float a = boost::any_cast<float>(params[0]);    float b = boost::any_cast<float>(params[1]);    consolePrint(a + b);}consoleFuncs["addAndPrint"] = boost::bind(&consoleAddAndPrint, _1);consoleFuncs["terminateApp"] = boost::bind(&consoleTerminateApp, _1, theApp);consoleFuncs["setRenderMode"] = boost::bind(&consoleSetRenderMode, _1);void onConsoleEnter(const std::string &line){    std::string command = getCommand(line);    parameters params = parseParameters(line);    consoleFuncMap::iterator i = consoleFuncs.find(command);    if (i != consoleFuncs.end()) (*i)(params);    else consolePrint("no command \"" + command + "\"");}


etc.
Hodgman
Hodgman
Quote:
Original post by xylent
I have read the boost::function documentation before posting here though. They don't quite solve my problem, if I understand the documentation correct and completely.

What I really want to do is, let there be two functions such as:

void setRenderMode(int renderMode);
bool terminateApplication();

What I'm trying to do is to store function pointers to both of the above functions in the same container.

You need boost::function AND boost::bind. Bind creates the "wrappers" (functors/function objects) for each of your functions automatically. The parameters that you pass in to bind (e.g. below, one call uses "this, 3, 7") are stored inside the returned function object.

typedef boost::function<void()> GenericCallback;std::vector<GenericCallback> vecCallbacks;void globalFunction(){}class Test{	public:	void memberFunction()	{	}	void memberFunctionWithParams( int x, int y )	{	}	void Register( std::vector<GenericCallback>& vecCallbacks )	{		vecCallbacks.push_back( boost::bind( &globalFunction ) );		vecCallbacks.push_back( boost::bind( &Test::memberFunction, this ) );		vecCallbacks.push_back( boost::bind( &Test::memberFunctionWithParams, this, 3, 7 ) );	}};


[EDIT]nullsquared beat me to it ;)
Antheus
Antheus
Quote:
vecCallbacks.push_back( boost::bind( &Test::memberFunctionWithParams, this, 3, 7 ) );

This doesn't solve the problem of how 3 and 7 come to be.

A console provides (not going into who regex complications) a string. This string has the format of "name p1 p2 p3 ... pn".

name maps to callback, which will invoke a function using p1..pn parameters. ideally, we want type safety (else we could just pass varargs).

All it comes down to now is syntactic sugar. Given a string x, we need to invoke a function with parameters P1..Pn. This requires, in part, reinvention of bind.

The basic idea is something like this:
struct Functor {  virtual void invoke(const std::string &) = 0;};
We assume there is a tokenizer present, which can parse the string parameter into individual parts (perhaps space separated.
Next, we need n specializations for each number of arguments:
template < class P1 > struct Functor1 : public Functor {  boost::function< void(P1)> cb;  virtual void invoke(const std::string &s) {    Tokenizer t(s);    P1 p1 = t.parse<P1>(1);    cb(p1);      }};template < class P1, class P2 > struct Functor2 : public Functor {  boost::function< void(P1, P2)> cb;  virtual void invoke(const std::string &s) {    Tokenizer t(s);    P1 p1 = t.parse<P1>(1);    P2 p2 = t.parse<P2>(2);    cb(p1, p2);      }};// and so on, up to N

This takes care of invocation. When a new line is available, we lookup the functor, and call f->invoke(console->current_line());

What remains, is syntactic sugar that will bind arbitrary function without requiring us to specify parameters types by hand. This is essentially what bind acomplishes.
template < class P1 > Functor * cbind( const boost::function< void(P1) > & f) {  return new Functor1<P1>(f);}template < class P1, class P2 > Functor * cbind( const boost::function< void(P1, P2) > & f) {  return new Functor2<P1, P2>(f);}// and so on...


Now, hopefully (since I'm typing blind here), this would allow us to do something magical like this:
void consoleCommand(int x, int y, const std::string & z);...Functor * f = cbind(&consoleCommand);f->invoke(console->current_line());
But I'm not sure what function's signatures are, and whether it would auto convert.

The returned value needs to be a pointer, since we need polymorphic behavior. Boost::bind (IIRC) isn't directly capable of mapping to our type, so we can't use that directly. The types of function parameters above should be well defined so that compiler can resolve them using templates. Boost PP can be optionally used to help with generation of all n versions of above helper pairs.

Final functor table would then be a std::map, and would be used simply something like:
Tokenizer t(console->current_line());callbacks[t.parse<std::string>(0)]->invoke(console->current_line());


The tokenizer in the example is:
// returns token at index, attempts to cast it to type Ttemplate < class T > Tokenizer::parse(int index);


The above can be coded fairly trivially (I've used such abstraction myself), but the short version here is likely to contain a gotcha or two.
xylent
xylent
Thanks to everyone for replies. Antheus' solution seems to be exactly what I'm looking for. I'll try it as soon as possible.

Thank you!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.