• Advertisement

Archived

This topic is now archived and is closed to further replies.

Function pointers and C++ classes

This topic is 6445 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there ! I''m currently building a console system for my engine. I''m able to get a command line from the user, but the only problem is mapping the command to a function... I''m using classes for all parts of my engine. What I want to do is be able to do something like this in my engine initialisation : m_Console.AddCommand( "TheCommand", Class.Function ); Then the CConsole::ExecuteCommand( string strCommand ) would call the good function for each command. The problem is how can I use pointer to members functions in my classes ? I think that I could set these functions as static but is there any other way ? Thanks for the help !

Share this post


Link to post
Share on other sites
Advertisement
You could create a hash table for that purpose... If you wanna learn how to do that, just search for hash tables on the net...

you could also create a structure that contains the command string, and a pointer to the function... and maybe ask from the function to give you an index or something when it places the command string and the function in a table... you can easily call it later on... there are many ways to do that... just choose which one best suits you...

Share this post


Link to post
Share on other sites
I know that I will have to use a structure to hold the commands and the functions pointers. My problem is how to obtain pointer to classes functions that are not declared as static ? Is it possible ?

Share this post


Link to post
Share on other sites
quote:

What I want to do is be able to do something like this in my engine initialisation :

m_Console.AddCommand( "TheCommand", Class.Function );



Looks just like Visual Basic Script inside of Microsoft Excel.

It is possible.
You probably don't want this in a game engine, but if you are using MS VC++ pro edition, you can try adding a COM object into your project for each class object you have. From the Insert menu, click on Insert New ATL Object. Then follow the wizard that will generate code and add garbage to your project. Now you can edit the interface of that object using IDL (Interface Definition Language).

This will instantly makes your .EXE scriptable the way you are asking! (Nevermind the agonizing obscurity, the impenatrable overhead or the loss of flexibility of rewriting your program.)




Edited by - Marsupial Rodentia on June 27, 2000 2:49:43 PM

Edited by - Marsupial Rodentia on June 27, 2000 2:51:42 PM

Share this post


Link to post
Share on other sites
Try this:

        
typedef void (Class::*memberPtr)();

void CConsole::AddCommand( string strCommand, Class* obj,
memberPtr cmd )
{
// associate obj and cmd with strCommand

}

void CConsole::ExecuteCommand( string strCommand )
{
// use strCommand to retrieve obj and cmd


// now invoke the command

(obj->*cmd)();
}

m_Console.AddCommand( "TheCommand", &object, &Class::Function );


If the command doesn't need access to a particular instance of your class, then you're probably better off using static member functions. If you want separate classes to add their own commands, then static functions are almost surely the way to go. That way, AddCommand can just accept any C-style function pointer of the appropriate type.

For another example, take a look at OpenUT/Core/Inc/UnScript.h in the CVS repository for the OpenUT project. The Unreal engine uses pointers to non-static member functions to support native functions in UnrealScript.

A couple things to keep in mind if you do use the non-static approach:

1) Never try to treat a non-static member function pointer as a C-style function pointer, or vice-versa. Only static member functions work that way, since they don't have an implicit "this" argument.

2) Don't cast a non-static member function pointer to any other pointer type. A non-static member function pointer is a 32-bit value to Visual C++, but gcc uses a 64-bit representation. You may not plan to use gcc, but it's probably best to stay on the safe side and avoid any unnecessary assumptions.

Good luck with your project!

Edited by - EvilTwin on June 27, 2000 4:33:23 PM

Share this post


Link to post
Share on other sites
quote:
Original post by Bob Jelly

Hi there !

I''m currently building a console system for my engine. I''m able to get a command line from the user, but the only problem is mapping the command to a function...


EvilTwin already showed how you should probably use a typedef to define what the member functions to be stored. You can''t really go storing members of all different types of classes here, as that would really complicate things, so I''m assuming these member functions are all members of the same class.

But the way I would do it would be to use an STL Map class.

    
#include <map>

class MConsole
{
private:
typedef std::map<string, memberFuncPtr> StrFuncMap;

StrFuncMap CommandMap;

public:
void AddCommand(string cmdname, memberFuncPtr p)
{
CommandMap[cmdname] = p;
}

void ExecuteCommand(string cmdname)
{
StrFuncMap::iterator i = CommandMap.find(cmdname);
if (i != CommandMap.end())
{
funcPtr = (*i).second();
// Call the command on ''obj''

(obj->*funcPtr)();
}
else
{
// command not found

}
}
};


Some of that may not be 100% accurate since I''m doing this from memory. If you need to pass in class instances, you might need to store a "struct { class* instance, memberFuncPtr ptr }" instead of just the pointer. Either way, I just put ''obj'' in the above code, you need to work out how to get that to be the right instance, if applicable.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I still don''t see why you can''t just pass the "this" pointer into the function implicitly.

typedef void (*CMDHANDLER)(Class* pThis)
void CConsole::Invoke (PTSTR pszCommand)
{
CMDHANDLER pfnCmdHandler = ...; // Get function pointer
pfnCmdHandler (m_pClassPtr); // Assume that the class pointer is a member of CConsole
}

void Class::HandlerA ()
{
// This handler executes
}


Share this post


Link to post
Share on other sites
quote:
you might need to store a "struct { class* instance, memberFuncPtr ptr }"


a STL pair would be better:

(using namespace std)


pair< Class*, MEMBERFUNCPTR > A(classInst, memberFunc);


that would make the command map defined as:


map< string, pair< Class*, MEMBERFUNCPTR > >


use some typedefs to clean it up, but you see what im getting at.

Share this post


Link to post
Share on other sites
Anonymous Poster -- You can do that, but you could only assign a static member function to pfnCmdHandler. If you want to end up calling a non-static member function, you''d need to do something like this:

    
static void Class::StaticHandler( Class* pThis )
{
if( pThis != NULL )
{
pThis->NonstaticHandler();
}
}

void Class::NonstaticHandler()
{
// Do the real work.

}

Share this post


Link to post
Share on other sites
EvilTwin:

"2) Don''t cast a non-static member function pointer to any other pointer type. A non-static member function pointer is a 32-bit value to Visual C++, but gcc uses a 64-bit representation. You may not plan to use gcc, but it''s probably best to stay on the safe side and avoid any unnecessary assumptions."

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.

Mansupial Rodentia:

There is really no need to use ATL or out-of-proc servers when working with COM. Smaller in-proc servers that use native COM interfaces are preferrable.



VK

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 <iostream>
#include <string>
#include <map>

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<string, 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<string, 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

Share this post


Link to post
Share on other sites

  • Advertisement