function pointers... [SOLVED]

Recommended Posts

hey all, i'm now trying to create a queue sort of system for actions that an object in my engine can use. basically it should take a function pointer and a list of the variables that should be passed when the function is called. i've had another method half working but it was using a bunch of pre-defined variables in a single struct (so like 3 floats and a string, etc.) and a constant value that was used to determine the function that was used to add the action to the list... anyway it was very confusing, very inefficient (for me to code) and a huge hassle to use. so now i've looked into function pointers... the problem i have is the fact that they are so rigid; everything has to be exact and that is making things difficult for the situation. so my question is this: is there any way (like tricks, library, etc.) that i can create a function pointer that can point to any function no matter what the return and/or the parameters? as far as i'm aware no such thing can be done but i wanted to see if anyone else could advise me otherwise or give some advice. thanks! [Edited by - the_moo on May 14, 2006 10:02:22 AM]

Share on other sites
If you don't know exactly which function you're calling, then you really wouldn't know what type of data to pass into it, right?

The method of queueing up function and parameter/return value heap pointers should most definitely work. What problem are you running into here?

I have constructed something like this to iteratively execute user-defined expressions, using a math library's functions to perform the operations. Did you want to look at the source?

Maybe you need to look at your situation a little differently, with the end solution in mind. Or describe it here in further detail for others to analyze.

Cowpower!

Share on other sites
boost::function, although its still going to require a static (i.e., compile-time) function signature. The boost::bind library will let you create "closures" of sorts to transport one function type to another. It's not going to be exactly what you want, however.

The typed nature of C++ makes what you want to do particularly difficult. It can be done but you'll be doing quite a bit of legwork and potentially some very un-typesafe hacks. Generally, its not a good idea and it isn't even required -- you just think you want it. It's a much cleaner design to simply fix the signature of the callbacks passed into your library.

The reason "runtime-typed" function pointers are basically useless in this situation is its not exactly clear what your library is supposed to do with the function. Let's say you do manage to implement a function pointer construct that does what you want. Your library then takes an instance of this super-function-pointer and stores it, to invoke later as a callback. What parameters will your library pass to the function? It has no idea what to pass and consequently can't pass anything meaningful. If it DOES know what to pass than chances are your library is too tightly coupled with the client code, or the library could have just made the function signature static since it knew exactly what information it was going to pass.

Share on other sites
I'm not an expert, but i have two ideas for this:

1) use functions like this one:
void* func(void*);
this is unsafe, but if you code it carefuly - you'll have lots of typecasts, and everything will work.

2) use functor objects, all implementing this interface:
class FunctorBase {
public: virtual void Execute () = 0;
};
Now, parameters can be passed to constructors of those functors. Or some set/get methods to set params/get result.

Maybe someone else has more ideas?

Share on other sites
I was thinking about function objects too. You won't be able to execute the function "regardless of the return type", as you mention somewhere, but that's impossible anyway. You can't call a function and not know what it will return, unless you also want to use variants for this.

Share on other sites
ok i've now got everything working but the assigning of the function pointers through a function parameter. i was just reading all the restrictions of member function pointers and am guessing that i won't be able to do what i want now...
see, the function to "queue" an action is a member function of my object class (and so is the queue list variable for all actions). i want to be able to assign any function pointer when queuing(??) from any classes that i may come up with outside of my engine.

now i know that this isn't sounding good so if it can't at all be done that's fine, but then i'm back to the same spot as before...

thanks again,

Share on other sites
Once again, this is something that boost::function and boost::bind can help you with.

Share on other sites
If you're in C++, use objects instead.
If you're in C, you'll have to cast from void*

Share on other sites
One solution would be to return a bool or int for a success code, passing in a single const reference to an object of type Uberclass.

This class would contain all of the variables available system-wide, providing massive function abstraction for a reasonable cost in memory -- 2GB RAM is common these days!

It is very handy for writing plug-in shaders in C++.

I doubt you're writing a math class, but
#include <string>using std::string;#include <iostream>using std::cout;using std::endl;class Uberclass{public:	int ia, ib, ic, id;	float fa, fb, fc, fd;	size_t la, lb, lc, ld;	string sa, sb, sc, sd;	int iadd(void)	{		return (ic = ia + ib);	}};int main(void){	Uberclass u;	u.ia = 4;	u.ib = 2;	int answer = u.iadd();		cout << answer << endl;	cout << u.ic << endl;	return 0;}

[Edited by - taby on May 1, 2006 9:36:48 PM]

Share on other sites
A possible way around it (only really feasible assuming your using standard datatypes, like unsigned int, float etc...) is through the use of unions.

union UberReturn{	float		fdata;	unsigned int	uintdata;	char		cdata;};UberReturn uberFunction ( void* whatever ){	UberReturn val;	val.fdata = 0.0f;	return val;}typedef UberReturn (*uberFunctionPointerType) ( void* whatever );

If you need to implicitly know what its returning, then the union can be wrapped in a struct along with an enumeration too.

Share on other sites
ok, i'm now trying to use boost::function and am only having one problem with it. when i try to compile the only error messages i get are like this:

In static member function static R boost::detail::function::function_obj_invoker2<FunctionObj, R, T0, T1>::invoke(boost::detail::function::any_pointer, T0, T1) [with FunctionObj = boost::_mfi::mf4<void, MeshInstance, float, float, float, bool>, R = int, T0 = ObjectX*, T1 = std::list<void*, std::allocator<void*> >]':

479 D:\Program Files\Dev-Cpp\Other Libs\Boost\include\boost-1_33_1\boost\function\function_template.hpp
instantiated from void boost::function2<R, T0, T1, Allocator>::assign_to(FunctionObj, boost::detail::function::function_obj_tag) [with FunctionObj = boost::_mfi::mf4<void, MeshInstance, float, float, float, bool>, R = int, T0 = ObjectX*, T1 = std::list<void*, std::allocator<void*> >, Allocator = std::allocator<void>]'

and there's about 10 of them all with slight variances to the error but all "instantiated from 'boost::function2'".

The line which causes these errors to show is:
QueueAction(&MeshInstance::Move,args);

the prototype for this function is:
typedef boost::function<int (class ObjectX* classtype, list<void*> args)> QueueFunc;
void QueueAction (QueueFunc func, list<void*> args);

basically MeshInstance::Move is the function that i'm trying to create a pointer to.

i have never seen this type of error before but i'm assuming it has something to do with templates.

if anyone has any idea and can help me... again... i'd be very grateful

thanks,

Share on other sites
It looks like you're missing part of the error message. After those, there's generally a message about what is actually wrong.

Anyways, this works for me [gcc 3.3.5]:
#include <boost/function.hpp>#include <iostream>#include <list>using namespace std;class X{};class MeshInstance{public:static int Move(X *objx, list<void *> args){    cout << "In Move\n";}};struct Command{    boost::function<int(X *,list<void *>)>   fobj;};int main(){    Command        cmdobj;    X              *xobj=new X();    list<void *>   voidlist;    cmdobj.fobj=&MeshInstance::Move;    cmdobj.fobj(xobj,voidlist);}

the first thing I'd check is that MeshInstance::Move actually returns an int, and not void.

And once you have it working, you might want to think about changing your design so that list<void *> is no longer necissary.

[Edited by - Telastyn on May 3, 2006 11:24:01 PM]

Share on other sites
An alternative (and potentially much cleaner) solution to this is to employ the Command pattern. The way it works is fairly simple:

• Define a Parameter type. The simplest type you might want to use (that is still flexible) might be boost::variant. You can also use a std::pair<std::string, boost::variant> to implement named parameters - which personally I'd highly recommend. (Another solution to named parameters is to use the name as an index in a hashmap.)

• Define an abstract base class Command that has a pure virtual function Execute and exposes a set of Parameters.

• Define a concrete subclass for each individual command you want to support, e.g. MakeThingExplodeCommand. Override Execute in each subclass to look at the parameter set and act accordingly.

• You can now queue references to Command objects and Execute them safely without knowing any details about them, thanks to polymorphism.

Share on other sites
You should post the rest of one of the error messages, and also the code where you are actually getting the error.

Share on other sites
Quote:
 Original post by ApochPiQAn alternative (and potentially much cleaner) solution to this is to employ the Command pattern. The way it works is fairly simple:Define a Parameter type. The simplest type you might want to use (that is still flexible) might be boost::variant. You can also use a std::pair to implement named parameters - which personally I'd highly recommend. (Another solution to named parameters is to use the name as an index in a hashmap.)Define an abstract base class Command that has a pure virtual function Execute and exposes a set of Parameters.Define a concrete subclass for each individual command you want to support, e.g. MakeThingExplodeCommand. Override Execute in each subclass to look at the parameter set and act accordingly.You can now queue references to Command objects and Execute them safely without knowing any details about them, thanks to polymorphism.

Quoted for emphasis.

There is also boost::any in addition to boost::variant. I don't really know the differences and pros/cons, but it's probably worth looking at both.

Share on other sites
Quote:
 Original post by ZahlmanThere is also boost::any in addition to boost::variant. I don't really know the differences and pros/cons, but it's probably worth looking at both.

The difference between the two is:

• boost::any can be an infinite number of types but only one type at any given time, boost::variant can only be a finite set of types and as with boost::any only one type at any given time.

• boost::any types do not need to be known up front while for boost::variant they do.

• boost::any uses a technique called automatic type erasor, basically internally boost::any at the very last moment erases static type information into a base type. For primitive operations such as copying delegates to virtual member functions of the base type for other operations clients have to use type switching and/or casting.

• boost::variant internally uses unions and metaprogramming to keep the static type information, type dispatch/resolution can be done at compile-time aswell as run-time.

• boost::variant is more type safe than boost::any

So boost::variant is more efficent while boost::any has more flexiablity when it comes to unknowns. Both can represent the samethings such as recusive types/data structures like cons lists or trees but with boost::variant you get sort of pattern matching using static visitors with boost::any you have to resort to runtime type queries. So i would make boost::variant the default choice unless you do not know what and/or the number of types you will be dealing with in advance.

Share on other sites
Telastyn, ApochPiQ, jpetrie, Zahlman and snk_kid: thank you all so much! all of you have given great advice (and so much of it [smile]). as soon as i get the chance i'm going to put all the information together and use it to come up with my preferred method for attacking this [grin]!

thanks a heap once again! i'll be sure to ask again if there's anything i'm still struggling with (especially with boost:: ... i've never used before now so it's all a bit dawnting(??)!!)

thanks once more,

Share on other sites
hey again,
ok i've got the game compiling now but i think there is a problem with one of my casts (which to be honest i don't like doing but i can't see any other way...).

for some reason i can't post large code chunks... so my next post has all the code that i would've had here; please take a look at it now if you're trying to help me out because the rest of this post will make sense then. thanks [smile]

ok i know that's a lot of code but i want to get this now that i feel i'm so close!
firstly i'm pretty sure the problem stems from the QUEUEITEM.customfunc member and i think that problem is caused by boost::any being the function pointer paramter...
**1: ObjectX is the base class i derive all my mesh and sprite based entities from (and those get derived from too). i've left out most variables but the one concerning this problem to make things clearer.
**2: i've made ExecuteAction pure virtual because when calling queued functions i need to have a pointer to the instance of the class that is actually calling it (so this works fine) so that any function being called will be found (so if i have a pointer to an ObjectX instance it will call the highest order ExecuteAction and all member functions will still be found).
**3: MeshInstance::QueueAction is the problem at the moment. the implementation of it is shown and line **4 shows the exact line:
**4: this is the line that is causing my game not to crash as such, but just quit out rather quickly with no error message. if i remove this line, everything works fine (i've yet to place ExecuteAction into my game so it is never called - no point till QueueAction works). now i've tried casting "func" as well but that caused more compile time errors like before.

i guess i'm asking: is there any way i can have a "customfunc" variable type that supports casting to the correct function pointer type when calling and assigning the function?
again i realise this is quite a bit to ask but it's the ideal way that i can visualize to solve this problem.

[Edited by - the_moo on May 8, 2006 3:52:48 AM]

Share on other sites
typedef boost::function<void (boost::any classtype, list<void*> args)> QueueFunc;

typedef struct QUEUEITEM {
QueueFunc customfunc;
list<void*> args;
};

class ObjectX { //**1
friend class DirectX;
protected:
list<QUEUEITEM> action_list;

public:

ObjectX ();
void ClearQueue ();
virtual void ExecuteAction () = 0; //**2
};

class MeshInstance : public ObjectX {
public:
MeshInstance ();
void SetBaseMesh (MeshX *basem, std::string meshname);
MeshX* GetBaseMesh ();
void Move (float x, float y, float z, bool world = false);
void QMove (list<void*> args);
void SetPosition (float x, float y, float z);
void SetColour (int alpha, int red, int green, int blue);
void SetScale (float width, float height, float depth, bool inc = false);
void SetAnimation (std::string animname);
void UnsetAnimation ();
void Rotate (float angx, float angy, float angz);
void RenderPass (LPDIRECT3DDEVICE9 pd3ddev, int pass);
void Render (LPDIRECT3DDEVICE9 pd3ddev);
void QueueAction (boost::function<void (MeshInstance* ptr, list<void*> args)> func, list<void*> args); //**3
virtual void ExecuteAction ();
bool Release ();
};

void MeshInstance::QueueAction(boost::function<void (MeshInstance* ptr,
list<void*> args)> func, list<void*> args)
{
QUEUEITEM item;

item.args = args;
boost::any_cast<boost::function<void (MeshInstance* ptr, list<void*> args)> >
(&item.customfunc) = func; //**4

action_list.push_back(item);
}

void MeshInstance::ExecuteAction()
{
boost::function<void (MeshInstance* ptr, std::list<void*> args)> funcptr;
funcptr = boost::any_cast<boost::function<void (MeshInstance* ptr,
std::list<void*> args)> >(action_list.front().customfunc);
funcptr(this,action_list.front().args);
action_list.pop_front();
}

Share on other sites
Quote:
 Original post by the_moo**4: this is the line that is causing my game not to crash as such, but just quit out rather quickly with no error message.

Read as: Crash without diagnostic message.
Best guess as to cause: it's throwing a boost::bad_any_cast.

Then again my brain is having major problems grokking that code. Please use [ source ] tags in the future...

EDIT: There we go, brain parser click.

The any_cast isn't doing what you're expecting.

Each boost::function< ... > is a full blown type.

So when you do:

boost::any_cast<boost::function<void (MeshInstance* ptr, list<void*> args)> >(&item.customfunc) = func; //**4

You're converting:
boost::function<void (boost::any classtype, list<void*> args)> *    (a pointer to a boost::function<>)=> boost::any=> boost::function<void (MeshInstance* ptr, list<void*> args)>   (by-value boost::function<> of different type)

Which isn't valid.

That entire line is messed up, as is the definition of func. You're going to need to write a translation function or change func's definition, and thus how you'd write func. For best results, you'd probably be putting the any_cast inside of the function that you want to pass... e.g.:
void ExampleFunc( boost::any first , list< void * > args ) {     MeshInstance * mesh = boost::any_cast< MeshInstance * >( first );     ...}

Then, you can call:

QueueAction( & ExampleFunc , ... );

Where QueueAction has:

QueueFunc func,

as that argument's type.

[Edited by - MaulingMonkey on May 8, 2006 10:51:35 AM]

Share on other sites
You might find the code in the thread C++ metaprogramming help (in the name of all holy, this is mind boggling) to be helpful. It allows something similar to what you want, I think. Since it uses boost::variant, it's restricted in the number of types it can deal with, but I think it'd be possible to make something similar using boost::any

Share on other sites
just saying a big thanks for the help from all of you! i looked at the post that Extrarius pointed me to and that made me realise i needed to read a little more about some stuff with templates. anyway i got it working pretty quickly after that so thanks again!

Create an account

Register a new account

• Forum Statistics

• Total Topics
627737
• Total Posts
2978873

• 10
• 10
• 21
• 14
• 12