Skip to main content
gamedev.net
🔒 Locked

Varable Length functions, function pointers, and trouble!

Started by Wolfman 13 replies 800+ views
Original Post
Wolfman
Wolfman
Hello, can someone please help me! Im trying to call a variable length function pointer from within a function that takes the function name, and the arguments from the function: typedef void (__cdecl CBaseClass::*runfunc) (int, ...); void runFunc(long time, CBaseClass *obj, runfunc func, int paramnum, ...) { va_list va_ArgumentList; if(paramnum == 0) return; va_start(va_ArgumentList, paramnum); (*obj.*func)(paramnum, va_ArgumentList); va_end(va_ArgumentList); } The func im testing it upon has the following signature: void __cdecl test(int n, va_list va_ArgumentList); And the call is: runFunc(1000, this, (runfunc)test, 3, 0.3, 0.6, 1.0); Unfortunatly when it is called, the function "test" gets garbled arguments to it (in va_ArgumentList). However, if I set up runFunc like this, it works: void runFunc(long time, CBaseClass *obj, runfunc func, int paramnum, ...) { va_list va_ArgumentList; if(paramnum == 0) return; va_start(va_ArgumentList, paramnum); float fl_Red = (float)(va_arg(va_ArgumentList, double)); float fl_Green = (float)(va_arg(va_ArgumentList, double)); float fl_Blue = (float)(va_arg(va_ArgumentList, double)); va_end(va_ArgumentList); (*obj.*func)(paramnum, fl_Red, fl_Green, fl_Blue); } Unfortunatly this is a bad solution, as in the final thing I will pass functions with varable lengths into runFunc. What do I do??? If you could help, thanks a lot for sparing the time and thought! -wolfman
-wolfmanThe End is only the Beginning of the End
IndirectX
IndirectX
What does ''test'' do? It should use the va_list provided, not attempt to retrieve the arguments itself. Besides, there''s no need to make test variable-argument function: it only accepts two arguments.
---visit #directxdev on afternet <- not just for directx, despite the name
Wolfman
Wolfman
Well what I want to do is to pass lots of different functions to runFunc, each will have different numbers of arguments.

Thus the function pointer param in runFunc will have have "..." as a param, otherwise the function pointer will be of the wrong type to handle all of the functions, and thus the functions to be passed need this in order to be of the right type.

-wolfman


-wolfmanThe End is only the Beginning of the End
Dean Harding
Dean Harding
Your function pointer is declared like this:

typedef void (__cdecl CBaseClass::*runfunc) (int, ...);

but the function you assign is declared like this:

void __cdecl test(int n, va_list va_ArgumentList);

See the difference? One takes a variable number of arguments, the other takes 2 arguments. What does the body of your test function look like?

Also, this just screams terrible design to me. I''d hate to come back to this after a few months and try to maintain it. There''s got to be a better way to do what you''re trying to do...


codeka.com - Just click it.
Wolfman
Wolfman
I tried test as void __cdecl test(int n, ...); and it gave me the same results.

I''ll try to explain what im doing, and yes, its probably the wrong way to do it, but I wanted to come up with my own design rather than use windows built in stuff.

Basicaly im trying to program my own demo, and what I want to do is to code up something that will enable me to set a time to call a function, and when the time is reached, that function is called. Then I want to pass lots of different functions to it so I can basicaly script my demo like so:

addTimedEvent(1000, this, (eventfunc)changeColour, 3, 1.0, 1.0, 0.0);

addTimedEvent(1000, this, (eventfunc)setSpeed, 1,500.0);

addTimedEvent(5000, this, (eventfunc)changeColour, 3, 0.0, 1.0, 0.0);

So I have a global event timer object that all my different demo classes can access, and add functions they have within them to the event timer. Is there a better way of doing this? I dont want to totaly hardcode the times into each seperate class, I want a centralised thing that controls it all. Ideas? Comments on my method?

Thanks,

-wolfman







-wolfmanThe End is only the Beginning of the End
Qw3r7yU10p!
Qw3r7yU10p!
i guess your''re programming solely in C

a C++ way to do it would involve functors (function objects) which can retain state information in the object and then be executed polymorphically with a call Execute().

i''m sure there''s plenty of info around about this
Wolfman
Wolfman
Actualy im programming soley in C++

I''ll take a look for some info on functors, thanks!

-wolfman

-wolfmanThe End is only the Beginning of the End
Wolfman
Wolfman
Hrm .... after having a look at functors, it seems I will have the same problem, ie functors require a fixed number of arguments to them. Thus, back to square one.

-wolfman

-wolfmanThe End is only the Beginning of the End
DrPizza
DrPizza
I''m unclear about what a __cdecl member function is. Where does the this pointer go?

Anyway, a va_list is not "...".

Your member functions need to take a va_list if you are wanting to pass them a va_list.

Anyway, you should use functors. The constructor of each object takes the parameter list, and the object has an operator()(void) method that does whatever action you want it to do.

char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;} /*** drpizza@battleaxe.net ***/
smart_idiot
smart_idiot
Try making a base class, and some other classes whos constructors take that arguments you need, and the class it''s self does the task you want. Then pass the pointers of the base object and then write ''even->DoYourThing();'' (which should be a virtual function, by the way.)

Or something. . .
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
fizban75
fizban75
How about you redesign things a bit differently.

Set up a global timer system. Then, in each object that you want to be based on a timer, have an "on_timer()" function. Register the class with the timer system to receive events at specific time intervals. The timer system will then call the class's on_timer() function at the specified time. How does that sound?

[edited by - fizban75 on June 14, 2002 12:33:10 PM]
Wolfman
Wolfman
Fizban, thats kinda what I want, but unfortunatly I want to call multiple functions in the same class. Thus I want the global system to call different functions in different classes multiple times, each with different parameters.

Thus I wanted to register a function with a global class, which was what my original post was trying to do.

DrPizza, the "__cdecl" is a compiler thing for VC++, that is needed to point to a function in a class. (If you dont include it, the compiler complains about the wrong pointer type).

I might have to end up with an ugly solution - calling a base class virtual function, which in a subclass is overidden, and then has a switch statement within it, so it does something like this:


  
void CClassDoesStuff::onTime(long time)
{
switch(time)
{
case(1000):
changeColour(1,0,1); break;
case(2000):
changeColour(1,0,0); moveTo(10,10,0); break;
case(4000):
scale(4); break;
default: break;
}
}



As you can see, doing this for each class is not the best way, as a complex series of events would be a nightmare to change if I wanted to "try something new" in the timeline.

-wolfman
-wolfmanThe End is only the Beginning of the End
IndirectX
IndirectX
quote:
Original post by Wolfman
the "__cdecl" is a compiler thing for VC++, that is needed to point to a function in a class.

Actually it''s calling convention specifier. __cdecl calling convention is required for variable-argument functions. You must match the calling conventions of function pointers and the functions they point to, but __cdecl isn''t needed to create a function pointer as such.
---visit #directxdev on afternet <- not just for directx, despite the name
smart_idiot
smart_idiot
I was thinking you should do something like the following. Maybe someone else can add to it.


  

class SomeAction
{
public:
virtual ~SomeAction();
virtual void DoIt();
};

class SetColour: public SomeAction
{
public:
SetColour(float cr, float cg, float cb): r(cr), g(cg), b(cb)
{}

virtual void DoIt(void)
{
// do something regarding setting a colour

}

private:
float r, g, b;
};

class Move: public SomeAction
{
public:
Move(float px, float py, float pz): x(px), y(py), z(pz)
{}

virtual void DoIt(void)
{
// do something regarding moving

}

private:
float x, y, z;
};


void DoAnAction(SomeAction &action)
{
action.DoIt();
}

int main()
{
DoAnAction(SetColour(2, 3, 5));
DoAnAction(Move(0, 0, 10));
return 0;
}

Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other

Topic Locked

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

Sign in to reply to this topic.