Sign in to follow this  
psyjax

More info, Function Pointers

Recommended Posts

Im currently porting my game engine from reglar C to a more Object oriented C++. The engine used to use structs and pointers to keep track of sprites which were stored in a linked list. The program would itterate thrugh the list and call each sprites DrawProc and MoveProc. These were function pointers. These are function pointers can be custome scripted by the user of the library. Thus I can make void MyDrawProc() { DrawItMyWay(); } sprite->DrawProc = MyDrawProc; This would call MyDrawProc() whenever the sprite was reached in the linked list. This was a beutiful system and it worked great. Howver, in C++ it is not this simple. Since Sprite is now a class, I can't seem to figure out a way to have a function pointer as a member mathod. How can I have it work similar to the example above? P.S. I read the recent fetured article, but it didn't help me very much :( Any help you guys could offer would be fantastic!

Share this post


Link to post
Share on other sites
Check out the stuff in "functional" for binding member functions as functors ... things like "member_func" and "ptr_member_func" or something like that ...

they are C++ standard library items and "The C++ Standard Library Reference" by Josuttis is a great reference to use to learn them ... but any you have that's fairly modern should work.

Share this post


Link to post
Share on other sites
You could force the user of the library to inherit from the Sprite class and supply his own draw() method.

That is, in your library:


class Sprite {
public:
virtual void draw() { // default implementation }
};


And then in the code that uses the library:

class MySprite : public Sprite {
public:
void draw() { // my new implemetnation }
};


And then in the source you maintain a list of sprite pointers and simply call their draw() methods. This is the standard way of doing things in C++.

If you want, you can also use member function pointers but I see no real reason to do that.

Edit: In many cases, virtual functions were introduced to replace the need for function pointers. If you really want to do this however, look into boost::bind and boost::mem_fun.

Regards,
Jeff

Share this post


Link to post
Share on other sites
If I do it using a derived class, will the linked lists of Sprites be able to call its method?

i.e.

class cSprite
{
draw()
}

class cMyCSprite:: cSprite
{
draw()
}

Now, the linked list is a linked list of cSprites not cMyCSprites, so will it know what to call?

Im not sure Im makeing it clear... sorry.

If you understand what I mean, let me know :D !

Share this post


Link to post
Share on other sites
Yes, it will, as long as your link list data is a cSprite*. This is what virtual function work in C++. For example,

cSprint* cs = linkList->GetElement(0);
cs->Draw();

C++ will dynamically bind function call to to cMyCSprite() in runtime.

I wish it helps!



Share this post


Link to post
Share on other sites
I came from C, and am not up on inheritance [hence I've run into a bunch of the problems you're running into...]

From what I know, if you make a list of the base class, it will use the base function [unless you typecast? That might not even work...]

In C++, function pointers are 2 different things, class function pointers which point to a class function, and 'normal' function pointers that point to a non-class function. The two are not interchangable [to my knowledge...]. You can't assign a normal function pointer a class' function and vice versa.

I don't use class function pointers, but since I'm ignorant of inheritance and virtual function goodness, I use other function pointers a bit in my game.

To put them into a class:



class foo{
private:
public:
void (*fp)(int,int);
}*fooptr;

fooptr=new foo;
fooptr->fp=some_function;
fooptr->fp(a,b);



so some_function cannot be a class function, but beyond that limitation, it should work like C.

Note that the 'normal' function also acts just like it would in C. It doesn't know anything about the class that calls it.

[And I'll warn you ahead of time, this leads to some nastiness that could probably be avoided by doing it the "right" way]

Share this post


Link to post
Share on other sites
One of the big things you need to "grok" when you move from C to C++ is the idea of deriving classes and polymorphism.

For example. In your C code, you had lots of different sprite structures. Each usually had the same function pointer pointing to some default draw/move function. But for different types of enemies or such, I assume you had different draw/move set for each. Right?

Here is an classic example of polymorphism. You might say something like:
DumbGrunt IS A sprite
SmartSniper IS A sprite
UberBoss IS a sprite.

When you have a IS-A relationship like this, it's a good place to use polymorphism. Define a base sprite class. This will be the "default" sprite class, if you will. Now, for those functions that are supposed to vary between each type or class of enemy, you must virtualize them. Then you can derive from the base class, make a seperate class for each type of enemy. Then just override whatever functions need to be. You dont have to override them all.

Now all you must do is make sure that your list holds all the sprites using pointers. Then when you call each ->draw() function, it will automatically at runtime figure out what type the class is and dispatch the call to the correct function: DumbGrunt::Draw, SmartSniper::Draw, etc. Be warned: polymorphism only works with pointers to classes.

There you go! When you use function pointers to member functions, you must preset what type of class the member function belongs to. You will find that member function pointers are pretty weak and unuseful. You can use boost::bind and such to get the job done, but I recommend using polymorphism beacuse in this case it's what it's designed for.

HTH

Share this post


Link to post
Share on other sites
you can do function pointers in C++



class CSprite
{
public:
static void MyDrawProc1(CSprite* pxThisSprite)
{
pxThisSprite->DrawItMyWay();
}
void DrawItMyWay()
{
}
void DrawItAnotherWay()
{
}

// that's the member variable, like in C.
void (*DrawProc)(CSprite* pxThisSprite);
};

void MyDrawProc2(CSprite* pxThisSprite)
{
pxThisSprite->DrawItAnotherWay();
}

//..
//..
//..

pxSprite->DrawProc = CSprite::MyDrawProc1;
pxSprite->DrawProc = MyDrawProc2;


it's just like in C really. The function as to be static to the class, not a member function.

Share this post


Link to post
Share on other sites
Original Poster: If you make the base class' function virtual then your code will "know which draw to call". That's the whole point of using inheritance and polymorphism.

Share this post


Link to post
Share on other sites
Hey all,

Thanks a bunch for stearing me in the right direction! So that what polymorphisim was for ;)

hehe...

Ya, I have just recently been doing OOP stuff, so I'm not totaly up on all the techniques. The books I have show examples of inheritance that werent particularly usefull. Thus, it left me with a feeling of "Ok, so what?". But now that a pratical example of how to use it has surfaced I will surely employ it more offten!

My linked lists are stored using the STL List template, so it shouldn't be a problem with the whole pointer thing considering that the STL List takes pointers anyway. A porgrammer friend of mind suggested that I should be able to typecast my derived class into a linked list of the parent class.

Anyway, thank you all very much.

Share this post


Link to post
Share on other sites
Quote:
Original post by psyjax
My linked lists are stored using the STL List template, so it shouldn't be a problem with the whole pointer thing considering that the STL List takes pointers anyway. A porgrammer friend of mind suggested that I should be able to typecast my derived class into a linked list of the parent class.


You don't even need to explicitly typecast:


// given this
class Sprite {};
class MySprite : public Sprite {};

// and this
std::list<Sprite*> AllSprites;

// this is perfectly valid code:
AllSprites.push_back(new Sprite());
AllSprites.push_back(new MySprite());


Just be careful you don't throw any pointers to temporary (or stack-based) sprite objects into the list (i.e. local Sprite variables) as you'll end up causing invalid access exceptions.

Regards,
Jeff

Share this post


Link to post
Share on other sites
Well, I did it!

I succesfuly implimented my move procs using inheritance. But now I got another issue. The linked list that holds the sprite will not call the derived classes virtual move proc.

Here is some code:



class cSprite
{
private:
//some data and crap...
public:
virtual void MoveProc( void ) {;}
};

class cSpriteLayer
{
public:
std::list <cSprite> SpriteList;
void DrawLayer( void );
};

void cSpriteLayer::DrawLayer( void )
{
list <cSprite> :: iterator cur_sprite;

for( cur_sprite = SpriteList.begin();
cur_sprite != SpriteList.end();
++cur_sprite )
{
cur_sprite->MoveProc();
cur_sprite->Draw();
}
}

//- Somewhere else in the code....

class cMan : public cSprite
{
public:
void MoveProc( void )
{
SetX(GetX()+0.1f);
cout << "I'm Moving!" << endl;
}
};


main()
{
//set up crap
cSpriteLayer TheLayer;
cSprite *spr = new cMan;

TheLayer.SpriteList.push_back(*spr);

gameLoop()
{
TheLayer.DrawLayer();
}
}



Why wouldn't this work?

I can call the move proc code from the derived class elsewhere in my code, but it does not get called in the linked list. What else do I need to do?

Does anyone know?

Thanx a bunch in advance. I really apreciate you alls help.

Share this post


Link to post
Share on other sites
SpriteList in cSpriteLayer has to be a list of cSprite*, not of cSprite. That's how the whole inheritance thing works. And then don't dereference the pointer when you're calling push_back().

When you do it the way you did, you push_back() a cMan object, not a cSprite pointer, so when it gets added to the list, it has to cast cMan down to a cSprite (which I didn't know you could even do). Since the list only contains cSprites, it's going to call the cSprite version of the method and not the cMan version.

Once they're pointers, inheritance can work its magic and it should all work out.

-Auron

Share this post


Link to post
Share on other sites
Yes, I thought you had stated your list stored pointers, but your code sample shows cSprite objects.

Also, make sure you delete anything that you allocated at end of program. It's up to you whether this is the responsibility of the cSpriteLayer (i.e. in its destructor run through all pointers and delete) or whether the client of the cSpriteLayer (i.e. the thing that creates the sprites in the first place) deletes the objects.

But they need to be deleted! Just like if you malloc something you must free it.

Regards,
Jeff

Share this post


Link to post
Share on other sites
Btw, what is this?


main()
{
...

gameLoop()
{
TheLayer.DrawLayer();
}
}


This looks like invalid code to me, some local function within the main() function?

Regards,
Jeff

Share this post


Link to post
Share on other sites
grrr

Thanks Auron, Im partly there.

I did your changes, but now it broke the itterator. Does the itterator have to be of cSprite* ?

I tried it both ways, but I cant seem to get it to work. Lots of garbled errors.

When I declare the itterator as a <cSprite> the compiler can't find the appropriate list functions, if it's the <cSprite*> the compiler cant find the appropriate sprite functions.

Any idea on this?

Share this post


Link to post
Share on other sites
hey ryper,

The code above is hevely abreviated :)

I just left in the important bits. Don't worry about memory and all that, Im takeing care of it, I just put the code in Im having trouble with.

As far as gameLoop(), I just put that there in place of the convoluted while/event/etc. crap I have there in the ACTUAL code heheh..

Im programming on my linux box, next to my G4 laptop, so I couldn't copy paste at the time. So you could imagine the trouble typing it all out, on this darn tiny keyboard too! heheh

Share this post


Link to post
Share on other sites

std::list<cSprite*> MySprites;

for(std::list<cSprite*>::iterator it = MySprites.begin(); it != MySprites.end(); ++it) {
cSprite* ptr = *it;
ptr->draw();
}


Iterators must match the container type. De-referencing an iterator produces the type that you inserted into the container.

I know, STL errors can be a bitch.

Regards,
Jeff

Share this post


Link to post
Share on other sites
rypyr!

Yahoo! You fixed it.

Now I can go to bed with an easy spirit :D

Seriously, I hate going to bed with coding problems on the brain, drives me nuts. Toss and turn, wake up screeming things like "It's the pointer! It's the pointer!"

hehehe

Anyway, thanks a bunch.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
btw, as to your original question, someone developed a really really cool way to make member function pointers and "normal" function pointers more compatible.

The article is fairly long but extremely informative, and at the end he wraps it up with his library for using delegates in an optimal way with a much simpler syntax than boost::bind.

Here's the address:
http://www.codeproject.com/cpp/FastDelegate.asp

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