🔒 Locked
Function pointers and C++ classes
Advertisement
Original Post
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 !
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...
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...
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
Try this:
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
|
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
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.
|
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.
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.
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:
|
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
"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
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:
|
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
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.
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
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
Why even bother with getting a pointer to a member function? Why not just use virtual functions, which are really just function pointers anyway.
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.
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:
Good Luck!
- null_pointer
Sabre Multimedia
Edited by - null_pointer on July 2, 2000 8:43:30 AM
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
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement