Skip to main content
gamedev.net
🔒 Locked

function pointers & classes

Started by _DarkWIng_ 25 replies 1.4k views
Original Post
_DarkWIng_
_DarkWIng_
I''ve got a class and one of the functions takes as a parameter a function pointer. This works just fine if that function is some global one. But if it''s a member of another class the it just doesn''t work. Any idea how to solve this? class Cubemap { void RenderToCubemap( void (*renderFunction)(void) ); ... } this works: Cubemap cm cm.RenderToCubemap( someGlobalFunction ); but this doesn''t cm.RenderToCubemap( someOtherClass.publicFunction ); You should never let your fears become the boundaries of your dreams.
You should never let your fears become the boundaries of your dreams.
Matt Calabrese
Matt Calabrese
quote:
Original post by _DarkWIng_
I''ve got a class and one of the functions takes as a parameter a function pointer. This works just fine if that function is some global one. But if it''s a member of another class the it just doesn''t work. Any idea how to solve this?


class Cubemap {
void RenderToCubemap( void (*renderFunction)(void) );
...
}

this works:
Cubemap cm
cm.RenderToCubemap( someGlobalFunction );

but this doesn''t
cm.RenderToCubemap( someOtherClass.publicFunction );



Type safety -- it''s the same reason you can''t pass a function with different parameters or that returns a value. It doesn''t match your declaration. Think about when you use a nonstatic member function of a class -- when you call it, you are implicitly passing the object you are using the function on to the member function (it''s just hidden from you).

A.MyMethod()

may look like it takes no parameters, but internally it''s passing the address of the object A to the function.

If you were to attempt to store the address of a member function to a global function pointer, just think of the problems you''d have -- you wouldn''t be able to associate the call with an object.

They are two completely different types of pointers, and you can''t directly convert between the two. If you want a pointer to a member function use the syntax

Returntype ( YourClasstype::*MethodPointerName )( explicit parameters );
_DarkWIng_
_DarkWIng_
Stoffel : TNX, but I already read that page and stuff describe there just doesn''t work for me.
Matt Calabrese : If I understand this right, I would have to create a function for every diferent class/function that has to call this function, or am i totaly off here? Would you be so nice and write a quick example?

You should never let your fears become the boundaries of your dreams.
You should never let your fears become the boundaries of your dreams.
Matt Calabrese
Matt Calabrese
Not for every method, just every method type, just like with functions. Here're some examples. If you need more to understand, just ask:


  class MyClass1
{
public:
void DoSomething() const;
int DoSomethingElse() const;
};

class MyClass2
{
public:
void DoSomething() const;
};

int main()
{
void (MyClass1::*SomeMethod)() const = MyClass1::DoSomething; // Will work

void (MyClass1::*SomeMethod)() const = MyClass2::DoSomething; // Will not work


void (MyClass1::*SomeMethod)() const = MyClass1::DoSomethingElse; // Will not work


return 0;
}


[edited by - Matt Calabrese on November 8, 2002 3:27:51 PM]
philscott
philscott
This is something I''ve been confused about. Am I right in thinking that the member function pointer can only be called from another member function?
Stoffel
Stoffel
No, but a member function pointer needs to have an object in order to be called. The difference between free functions and member functions are that the member functions have an "invisible" parameter called "this" passed along to it. It''s just that instead of passing it as an argument:
memFun (&object);
you pass it by "calling" the member on that object:
object.memFun ();

So if you use a member function pointer, you have to have an object and use the pointer-to-member operator:
(object.*memFunPtr) ();
Oluseyi
Oluseyi
This comes up every other week, doesn''t it? I wish the search were back up (hint: use Google with "site:gamedev.net").
_DarkWIng_
_DarkWIng_
OK, I understand now why I need ClassName::*functionName() stuff but the thing is it still doesn''t work. I just get different error.


Class B {
Call( void (*callie)(void) ) {
callie();
)
}

Class A {
void Render() {
// do some stuff here
}
void Play() {
// 1) original idea
instanceOfB.Call( Render );
// 2) idea from www.function-pointer.org
instanceOfB.Call( A::*Render );
// 3) from what all of you wrote
void (A::*myFunction)() = A::Render;
instanceOfB.Call( myFunction );
}
B instanceOfB;
}


What do I need to rewrite to make this work?


You should never let your fears become the boundaries of your dreams.
You should never let your fears become the boundaries of your dreams.
Oluseyi
Oluseyi
quote:
Original post by _DarkWIng_
OK, I understand now why I need ClassName::*functionName() stuff but the thing is it still doesn''t work.

Actually, you apparently don''t understand. Using your example, for B::Call to be able to accept one of A''s methods its argument (callie) would need to be of type (void A:: *)(void), not (void *)(void). No amount of syntactic gymnastics will alter that requirement.

Besides, what does A need B for? It can maintain a pointer to any of its own functions and call them directly. What is the real "problem" you''re trying to solve?
Matt Calabrese
Matt Calabrese
Nope, you're still not understanding.

There are a lot of errors in your example code, so I'm not going to touch on it directly considering it's hard to tell which were typos and which you really don't understand.

You can't convert from a member function pointer to a function pointer. We went over all of the reasons why above. If you STILL don't understand, just think about it like this -- a nonstatic member function needs an object to deal with because you will be using datamembers of an instance of the class. If you pass just the member function pointer and try to convert it directly to a regular function pointer, you are making it sytanticly impossible to call with an associated object (without a cast, which you should NOT do), and not only that, your program would most likely crash because any member data that the member function refers to would attempt to be accessed through garbage on the stack where the "this" pointer would normally be ( i think that's what'll happen, but you might even get an error prior ), much in the same way if you were to attempt to dereference a pointer (or worse, write to value pointer to by it) that you didn't initialize. Not good.

Just think rationally about what you're doing and think about why you can't do what you're attempting. Just imagine what's going on internally and you'll better understand it. We're explaining it as best we can, but unless you try to think about exactly what's happening you're just gonna have more an more questions. Read over what we've said a few times and really think about what's going on in memory and most of your questions you can probably answer yourself.

[edited by - MAtt Calabrese on November 9, 2002 4:34:29 AM]
_DarkWIng_
_DarkWIng_
OK, I give up. I "think" I understand why it doesn''t work. Member functions need some class(instance) to operate on. It can''t be just called on its own. Right? I still haven''t got a clue how to make it work. I''ll try to find another way.

"...What is the real "problem" you''re trying to solve?..." I was trying to make function to create(render) cubemap with a simple call cubemapInstance.RenderCM( worldRenderFunction,... ); So RenderCM(...) would just have to orient camera & call worldRenderFunction 6 times to create cubemap. Plain and simple but not as easy as I tought . Now I guess I will have to break apart RenderCM into a few functions and call them from other class.


You should never let your fears become the boundaries of your dreams.
You should never let your fears become the boundaries of your dreams.
Fruny
Fruny
Pointers to member functions are not pointers to function : the code

#include <iostream>

class Foo
{
public:
void foo() {}
virtual void bar() {}
static void baz() {}
};

void quux() {};

int main()
{
std::cout << sizeof( &Foo::foo ) << ' '
<< sizeof( &Foo::bar ) << ' '
<< sizeof( &Foo::baz ) << ' '
<< sizeof( &quux ) << std::endl;
}
prints out, on my system, 8 8 4 4 , proving that pointers to member function and pointers to function are inherently incompatible - they do not even have the same size.

No amount of casting will fix that.

- edit - to solve your problem, consider using function objects (a.k.a functors), since you control both ends of the API. boost::function is a good point to start - see boost link in sig.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on November 9, 2002 6:10:41 AM]
"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
Matt Calabrese
Matt Calabrese
quote:
Original post by Fruny
proving that pointers to member function and pointers to function are inherently incompatible - they do not even have the same size.
[edited by - Fruny on November 9, 2002 6:10:41 AM]



That part shouldn't be correct. A pointer to a location in memory is a pointer of a location in memory and always has the same size. Your compiler shold not return two different sizes unless it's on crack. I checked on msvc++ 6 just in case and it returns the same size as I expected. I'm not sure, but I think that it's standard that all pointers must be of the same size -- if not, then it still doesn't make sense why you would ever want them to be different sizes as it would be pointless. The only thing I can think of that might be happening is you compiler might be storing both the memory location and the integer vtable function pointer offset to account for polymorphism when using the method pointer, but I'm pretty sure that's NOT a part of the standard ( and shouldn't be, because then all of the methods in that class would have to take up the extra space even if they aren't defined as virtual so that they are the same size ). It would also be bad because since it's not a part of the standard (unless I'M the one on crack right now ), then that would mean compilers that follow the standard would only allow for static linking with method pointers (which they do) but the method I described would allow for dynamic linking, but it can potentially screw up the results if the programmer WANTED static linking. Anyways, I'm rambling on about what might be happening, but I'm honestly not sure. What Fruny is saying about the sizes of the pointers, to my knowledge, should not be correct. Anyone with another explanation, please bring it forward!


EDIT:

Hmm, I decided to search myself on the topic and already I'm getting VERY mixed results. One site claims that method pointers should actually take up 12 bytes on 32-bit address machines and that if they don't they aren't following the standard! They mention what I was talking about in my post about the vtable offset. If 3 * sizeof( void* ) is the standard, then I say f*ck the standard, I want static linking unless I say otherwise, and if I make a pointer I want a POINTER not all that other bullship to bloat my code. I sure hope they're not correct. Where's a place to find out EXACTLY what the standard is?

http://www.geocrawler.com/archives/3/361/1993/10/0/2026036/

[edited by - Matt Calabrese on November 9, 2002 7:01:04 AM]
Fruny
Fruny
The C++ Standard ( $18 in electronic form on ANSI's website ), makes no mention of how the language should be implemented, only of what the language is . vtbls, even though they are a de-facto standard, are never mentioned - you might find a compiler that doesn't use them.

C++ has no ABI - there is no constraint on the binary representation of language constructs - which is why I specified 'on my system' (g++ 3.2).

Pointers to member functions are not required to be implemented as addresses. They are often implemented as structs carrying type information in addition to the function address. Virtual function pointers hold an offset instead of an address, which could, once again, be of any size.

This brings us back to the topic of this thread - the lack of convertibility - which is explained by the fact that we are dealing with different entities (which, BTW, use different call syntax : quux() vs. f.*foo() )

g++ 3.2 on cygwin-386 yields 8 8 4 4
WorkShop Compilers 5.0 98/12/15 C++ 5.0 on Solaris 7 yields 8 8 4 4 (after tweaking around the lack of iostreams)
MIPSpro Compilers 7.2.1.2m on SGI IRIX 6.5 yields 12 12 4 4
Microsot Visual C++ 7 yields 4 4 4 4 (after tweaking around rejection of &Foo::baz and &Foo::quux in sizeof)

Edit - in reply of Matt's edit

Well, when you're taking a pointer to a variable, you do get a memory address. When you take a pointer to a member function, you do get a pointer to a member function - something that ACTS as you desire, not necessarily something that has the internal structure you expected it to have (don't even get started about the private members of classes. Just because you don't know why or what they are doesn't mean they're 'bloat').

So, let me put the emphasis on the fact that a pointer to a member function is not a 'memory address'. It is a pointer to member function. If you're unhappy with the way it is implemented, roll your own out, C style. Then compare the bloat, robustness and convenience of the two. Only then will you be entitled to scream about code bloat.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on November 9, 2002 7:34:29 AM]
"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
Matt Calabrese
Matt Calabrese
If that is true, then what is the standards claim on polymorphism through "method pointers?"

With these verying implementations, some account for it and some don''t! Compilers should be free to their own implementation, but whether or not method pointers work with polymorphism should not. I can only imagine the porting issues! What if you don''t want dynamic linking on an object through a method pointer call? There''s nothing you can do! They''ve gotta have some stand on it? IMO static linking is the best way to go on this -- if I want dynamic linking I''ll just use templated functors. I really don''t want to go and waste an extra 9 bytes per method pointer for dynamic linking when in many cases it isn''t necissary...
Oluseyi
Oluseyi
quote:
Original post by _DarkWIng_
I was trying to make function to create(render) cubemap with a simple call cubemapInstance.RenderCM( worldRenderFunction,... ); So RenderCM(...) would just have to orient camera & call worldRenderFunction 6 times to create cubemap. Plain and simple but not as easy as I tought . Now I guess I will have to break apart RenderCM into a few functions and call them from other class.

In other words, you want to call an externally defined function to operate on/using internal data.

typedef (void *RENDERFUNCTION)( long *, long * );
 
void worldRenderFunction( long * some_data, long * some_more_data )
{
// whatever
}
 
void Cubemap::Render( RENDERFUNCTION rf )
{
rf( data, more_data );
}

You can vary all the types and number of arguments; this is an abstracted example just to give you an idea of one way to do it. Another is using functors:

struct renderfunctor
{
virtual void operator () ( long *, long * ) = 0;
};
 
struct rendercubemap : public renderfunctor
{
virtual void operator() ( long * some_data, long * some_more_data )
{
// whatever
}
};
 
void Cubemap::Render( const renderfunctor & rf )
{
rf( data, more_data );
}
Oluseyi
Oluseyi
quote:
Original post by Matt Calabrese
If that is true, then what is the standards claim on polymorphism through "method pointers?"

With these verying implementations, some account for it and some don't! Compilers should be free to their own implementation, but whether or not method pointers work with polymorphism should not. I can only imagine the porting issues! What if you don't want dynamic linking on an object through a method pointer call? There's nothing you can do! They've gotta have some stand on it? IMO static linking is the best way to go on this -- if I want dynamic linking I'll just use templated functors. I really don't want to go and waste an extra 9 bytes per method pointer for dynamic linking when in many cases it isn't necissary...

http://cpptips.hyperformix.com/cpptips/ptmf

The canonical solution is to use functors anyway.

[edited by - Oluseyi on November 9, 2002 7:46:52 AM]
Fruny
Fruny
quote:
Original post by Matt Calabrese
If that is true, then what is the standards claim on polymorphism through "method pointers?"

With these verying implementations, some account for it and some don''t! Compilers should be free to their own implementation, but whether or not method pointers work with polymorphism should not. I can only imagine the porting issues! What if you don''t want dynamic linking on an object through a method pointer call? There''s nothing you can do! They''ve gotta have some stand on it? IMO static linking is the best way to go on this -- if I want dynamic linking I''ll just use templated functors. I really don''t want to go and waste an extra 9 bytes per method pointer for dynamic linking when in many cases it isn''t necissary...


First note - the URL you linked refer to a thread from 1993. The C++ standard was finalized in 1998 - five years later. It accurately referred to that as a bug.

Second note - templated functions are not dynamic typing (not ''linking'' either) - they''re compile-time polymorphism, static typing. Virtual functions are dynamic typing (though not to the extent of SmallTalk or Objective C)

Third note - on waste - How much do you think you ''waste'' from data alignment on qword boundaries that is mandated by 32-bit processors

There are no porting issues, since the C++ standard is a source specification. Theoretically (if we had standard-compliant compilers) standard C++ source should compile the same with any compiler. If you correctly referred to the size of a void* pointer as sizeof( void* ) instead of hard-coding 16, 32, 64 or whatever is currently valid on your platform, on your compiler, then you''re fine. This has always been true. The size of a short, int, long aren''t exactly defined either, only minimum sizes and their relative ordering.

Keep that in mind : the size of a type is sizeof Type, nothing else.

On the other hand, the lack of an ABI prevents binary interoperability - a library compiled with a given compiler will not necessarily link correctly with programs compiled with another compiler. You see that frequently even on gamedev.net when people want to use DirectX with g++ or Borland C++. It is a problem, which is not going away until and unless a C++ implementation distinguishes itself enough that others adopt its binary interface. Will it be MSVC, Comeau C++, Metrowerks Codewarrior, GNU C++, Visual Age C++, Intel C++ on the x86 ? I don''t know.

Finally, how do you enforce a binary API on different architecture (Motorola, Mips ...)

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]
"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
Matt Calabrese
Matt Calabrese
quote:
Original post by Oluseyi
The canonical solution is to use functors anyway.

[edited by - Oluseyi on November 9, 2002 7:46:52 AM]


Yup, that's what I was saying.

Anyways, is it specifically stated whether or not method pointers are supposed to work with polymorphism?

Edit: (too many of us are posting in here at the same time, I can't keep up with the edits and posting, etc. )

First off -- linking was a mistype, but it was a mistype for binding not typing (it's 4:45 AM here and i've been up for almost 20 hours stright ),

second, I said templated functor, not templated function. I wouldn't have gotten very far if I didn't realize templating was compile-time

When I was talking about portability I was refering to polymorphism with method pointers, but apparently it is standard to have it work with polymorphism, which is what I was asking earlier. If it wasn't then on some compilers virtual method calls would be staticly bound and on others, dynamically, which would be problemmatic. As I said, I now notice that it is standard that method pointers work with polymorphism, so everything works out fine and dandy.

Thanks for those responses, you actually answered a question that has been bugging me for a while but I never thought to ask for some reason.

Anyways, I think we got off-topic...

[edited by - Matt Calabrese on November 9, 2002 8:08:44 AM]

Topic Locked

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

Sign in to reply to this topic.