Function pointers and C++ classes

Started by
12 comments, last by Bob Jelly 23 years, 9 months ago
quote:
I agree, but I'm wondering why GCC uses 64 bits. All you need to call a procedure is 32 bits, after all. Couldn't you use inline assembly language for compatibility? In reference to your previous remark, how do C COM interfaces work, then? When you call COM interfaces in C, you are forced to deal with explicitly calling them through their VTBLs and sending the THIS parameter in first.


I don't know the details of how pointers to non-static member functions are implemented in the two compilers, but I'm glad you brought that up. I'd been kinda curious about the size myself, so I wrote a little test program to investigate, and I discovered that Visual C++ actually switches from 32-bits to 64-bits when you use multiple inheritance! So you have no way of knowing the size of (Class::*memberFuncPtr)() without looking at the class definition -- yet another reason not to try any casting tricks.

As for why you'd ever need more than 32 bits, the missing "this" pointer is actually more important than it seems. Suppose we have the following code:

                    class Class{public:    ...    virtual void MemberFunction();    ...}; void Class::MemberFunction(){    ...} typedef void (Class:*memberFuncPtr)(); void CallClassFunction( Class* pClass, memberFuncPtr pFunc ){    if( pClass != NULL )    {        (pClass->*pFunc)();    }} int main( void ){    Class myObject;     ...    CallClassFunction( &myObject, &Class::MemberFunction );    ...}                    


Notice that CallClassFunction takes a Class* argument. What if we create a subclass of Class, override MemberFunction(), then pass in (Class*)pSubclass? Whenever we call MemberFunction on a Subclass object, we expect to call Subclass::MemberFunction, so why should it be any different if we call MemberFunction through a non-static member function pointer? To keep the late-binding behavior of the virtual function, the non-static member function pointer can't simply contain the 32-bit address of Class::MemberFunction. To call the correct function, we need the correct vtbl, and we won't get that until we use the pointer with a specific object. No "this" pointer, no vtbl. Compiler writers need to pack enough information into a non-static member function pointer to find the correct function to call for any appropriate object. Apparently, the Visual C++ and gcc teams used slightly different approaches to the problem.

Fortunately, none of this applies to using COM interfaces in C. To access an interface, you need to know the address of a COM object so you can pass it as the "this" argument. You can get the vtbl from the "this" pointer, so all you need to do is choose your standard, 32-bit function pointer from the vtbl.

Non-static member function pointers may seem like more trouble than they're worth, but they're really not that bad as long as you don't do any inappropriate casting.

Edited by - EvilTwin on June 29, 2000 3:32:34 AM
Advertisement
A bit off the topic here...
BUT if you was using C++Builder, C++Builder implements closures
which are like function pointers but better - they implement a implimit (spelt wrong) this pointer, so that you don''t have to send in a this pointer:
typedef void ( __closure *Command)(CommandWhat Msg,int Arg1,int Arg2).
AExampleClass Class;
Command ACommand = Class.ACommand;
ACommand(CHANGEDEPTH,1234,5678); //calls Class.ACommand.

But that wasn''t any use, was it since you''re obviously using VC.
Hi there !

This is even a bit more off the topic,but would it be possible to dump out function data, and later on load it up again?
You could then set an objects function to another one which was saved before.

- PestilencE
- just to have something in here
Why even bother with getting a pointer to a member function? Why not just use virtual functions, which are really just function pointers anyway.


class Command
{
public:
virtual void Execute() = 0; // no code
}



Of course, many commands are going to need arguments, so you can just pass a string for that. Use an STL string& to make it extremely easy.


// excerpt from class Command:
virtual void Execute(const string& arguments) = 0;



Then just derive all of your commands from that class, provide an implementation for the virtual function, and pass in their pointers. No questions asked, no casting, it just works. In fact, use the map class (a linked list of pairs) to make it much easier.

Example:


#include &ltiostream>
#include &ltstring>
#include &ltmap>

using namespace std;

// same as before
class Command
{
public:
virtual void Execute(const string& arguments) = 0; // no code
}

// class that manages commands and parses the buffer:
class Console
{
public:
void Add(string text, Command* pCommand)
{ commands[text] = pCommand; } // add or change any command

map&ltstring, Command*> commands;
}

// all of the commands that we wish to use:
class ClearScreen
: public Command
{
public:
virtual void Execute(const string& arguments)
{ /* add code to clear the screen */ }
};

class ChangeTextColor
: public Command
{
public:
virtual void Execute(const string& arguments)
{ /* add code to change the text color */ }
};

// the entry-point
void main()
{
// create a console
Console the_console;

// create add some commands
the_console.Add("cls", new ClearScreen);
the_console.Add("ctc", new ChangeTextColor);

// do something...

// delete any commands that were added
map&ltstring, Command*>::iterator iterator;
for( iterator = the_console.commands.begin(); iterator != the_console.commands.end(); iterator++)
{
delete iterator.second; // delete the Command*
the_console.commands.erase(iterator); // remove the entry
}
}



Good Luck!




- null_pointer
Sabre Multimedia


Edited by - null_pointer on July 2, 2000 8:43:30 AM

This topic is closed to new replies.

Advertisement