Author Topic: Pointers to class functions  (Read 6034 times)

HellishER

  • Guest
Pointers to class functions
« on: April 06, 2006, 04:16:20 AM »
Hello, im want to be able to store pointers to member functions that are in diffrent types of classes within the same array,vector linklist etc.. Is it possible?
Or do all the classes that i want to store need to have the same base class?

the_viking

  • Jr. Member
  • **
  • Posts: 97
  • Karma: 2
    • View Profile
Re: Pointers to class functions
« Reply #1 on: April 06, 2006, 05:32:55 AM »
Pointers to class functions is a problem. You can only have function pointers for one class, but you can't use the same type of the pointer for another class, so you can't have methods that are of the same style but from other classes in the same array.

Of course, there is a workaround, but it involves assembly usage. You can look at the AngelScript source how to do it (http://www.angelcode.com).

I once tried the same (because I wanted something comparable to UnrealScript's states natively), but I ended up using a lot of switch()es to difer between the states rather than just use the state index to get the state method from a state method array.

pixelnation

  • Not-a-newbie
  • *
  • Posts: 37
  • Karma: 0
    • View Profile
Re: Pointers to class functions
« Reply #2 on: April 06, 2006, 07:36:49 AM »
I'm using this template system to store arbitrary wrapped data in a stdext::hash_map

Code: [Select]
#ifndef __EVENT_ITEM_H
#define __EVENT_ITEM_H

#include <string.h>

//***********************************
// A item in the event data map
// This is a base data type with a virtual to
// support dynamic casts and supports
// templated data containers
//***********************************
class EventItem {
 
  public:
    EventItem() {}
    ~EventItem() {}
   
  protected:
    virtual void LogContents(void) = 0;

};

//***********************************
// A template class for sending data in a map
//***********************************
template <class T>
class EventItemTemplate: public EventItem {
 
  public:
    EventItemTemplate(T newValue) : value(newValue) { }
   
    T GetValue() { return value; }
   
  protected:
    T value;
    virtual void LogContents(void) {  }
 
};

//***********************************
// Templated data types
//***********************************
#define IntEventItem EventItemTemplate < unsigned int >
#define StringEventItem EventItemTemplate < const char * >
#define BoolEventItem EventItemTemplate < bool >

#endif // __EVENT_ITEM_H

You just need to know what you are going to get out again then you can dynamic_cast it out.

Its probably a bit of overhead but makes my event system very simple...

Code: [Select]
//***********************************
// Fetches the event data pointer for the requested key or null
// if it doesn not exist
//***********************************
EventItem * EventData::GetEventItem(EventKey key) {
  EventDataMapIterator it = eventDataMap.find(key);
 
  if (it != eventDataMap.end()) {
    return it->second;
  }
  else {
    return 0;
  }
}

//***********************************
// fetches a const char *
//***********************************
const char * EventData::GetString(EventKey key) {
  EventItem * eventItem = GetEventItem(key);
  ASSERT(eventItem, STRING("Failed to locate event item for event key: %i", key));

  StringEventItem * stringEventItem = dynamic_cast<StringEventItem*> (eventItem);
  ASSERT(stringEventItem, STRING("Failed to cast event item to string wrapper for event key:  %i", key));
 
  return stringEventItem->GetValue();
}

My stdext::hash_map is:

Code: [Select]
#define EventDataMap stdext::hash_map < EventKey, EventItem * >
#define EventDataMapIterator EventDataMap::iterator

Its a variant pattern I suppose.  I don't know if you can use pointers to member functions in template definitions, I don't see why not?

Code has heaps of my macros for asserts and temporary string creation from a set of buffers etc, ignore that guff,

pn

Oliver Smith

  • Guest
Re: Pointers to class functions
« Reply #3 on: April 09, 2006, 08:11:07 AM »
There are some <algorithm> functions for this, such as memfun_ptr. But you have to remember that the address of a member function of an arbitrary class is a scary thing.

Honestly, we're giving you answers to a non-ideal solution. If you describe the problem you're using this solution to, maybe we can suggest a better solution :) In most cases the language already provides ways to work around the situation you're describing. It is possible to do something very like "Interfaces" in C++. It means a re-distribution of labor (there is more declarative work but less functional work for you to do). Check out the Scott Meyers C++ books.

HellishER

  • Guest
Re: Pointers to class functions
« Reply #4 on: April 10, 2006, 08:37:22 AM »
Have not hade time to try anything about this for some days now..
But what i want to do is to add pointers to functions in a list that will be "connected" to my console.. So i can just type "name Billy". Then it searches through the list for "name" add runs that function that are connected to that command with the parameter "Billy".

Oliver Smith

  • Full Member
  • ***
  • Posts: 173
  • Karma: 7
  • Oliver Smith
    • View Profile
    • Oliver's blog
Re: Pointers to class functions
« Reply #5 on: April 10, 2006, 04:26:40 PM »
To do that, all of your functions are going to need to conform to a standard fingerprint, e.g.

void myCommand(const char* arguments);

So at bare minimum you probably want to start with a typedef:

typedef void (COMMAND) (const char*);

The type name is "COMMAND", and you can create a quick hash-map lookup like this:

Code: [Select]
typedef bool (COMMAND) (const char*) ;
typedef std::vector< std::pair<const char*, COMMAND*> > COMMAND_INDEX ;

bool
myCommand(const char* arguments)
{
  printf("Your arguments: %s\n", arguments) ;
  return true ;
}

bool
doCommand(const COMMAND_INDEX& index, const char* command, const char* arguments)
{
  // Search the vector for a match,
  // or you could use a hash_map
  COMMAND_INDEX::const_iterator it ;
  for ( it = index.begin() ; it != index.end() ; ++it )
  {
    if ( strcmp(it->first, command) != 0 )
      continue ;

     // Found a match, lets call it
#ifdef VERBOSE
    COMMAND* functionPtr = it->second ;
    functionPtr(arguments) ;
#else
    return it->second(arguments) ;
#endif
  }

  printf("Command %s not found\n", command) ;
  return false ;
}

int
main(int argc, char* argv[])
{
  COMMAND_INDEX cmdIndex ;
  cmdIndex.push_back(std::make_pair("billy", myCommand)) ;
  doCommand(cmdIndex, "billy", "I wanted an argument") ;
}

That's a very "C" way of doing it, a better approach would be to use the STL definition for a Function Object, and encapsulate your command functions like this:

Code: [Select]
class CommandBase : public std::unary_function<const char*,bool>
{
public:
  CommandBase(const char* command) : m_command(command) {}
  const char* m_command ;

  // This is an abstract class, you need to define operator() yourself.
  virtual bool operator()(const char*) const = 0 ;
} ;

// Unary function that takes a single const char* argument and returns bool.
class BillyCommand : public CommandBase
{
public:
  // Tell our parent that we are invoked by the command "billy"
  BillyCommand() : CommandBase("billy") {}

  // Now define the actual code for this command
  virtual bool operator()(const char* arguments) const
  {
    printf("Your arguments: %s\n", arguments) ;
    return true ;
  }
} ;

// Again, you should probably use a hash_map but
// I don't want to guess which variant anyone has.
typedef std::vector<CommandBase*> COMMAND_INDEX ;

bool
doCommand(const COMMAND_INDEX& index, const char* command, const char* arguments)
{
  COMMAND_INDEX::const_iterator it ;
  for ( it = index.begin() ; it != index.end() ; ++it )
  {
    const CommandBase& cmd = *(*it) ;
    if ( strcmp(cmd.m_command, command) == 0 )
      return cmd(arguments) ;
  }
  printf("Command %s not found\n", command) ;
  return false ;
}

int
main(int argc, char* argv[])
{
  COMMAND_INDEX cmdIndex ;
  cmdIndex.push_back(new BillyCommand) ;
  doCommand(cmdIndex, "billy", "I wanted an argument") ;
}

There are still various ways you could improve on this, e.g. by making the command index static members of the CommandBase class, and then using the "static-in-static" trick to create an order-guaranteed constructor.

Code: [Select]
class Foo
{
...
public:
  typedef std::vector<Foo*> INDEX ;

  INDEX& static getIndex()
  {
    static INDEX s_index ;
    return s_index ;
  }

  void static addToIndex(Foo* foo)
  {
    getIndex().push_back(foo) ;
  }
...
} ;

The caveat to this trick is that s_index won't be constructed until the first time you call the getIndex function, which will be immediately before you need the object, i.e. in the addToIndex call.

By adding the following constructor to foo:

Code: [Select]
class Foo
...
  Foo(const char* command) : m_command(command) { addToIndex(this) ; }
...

class SuperFoo : public Foo
{
public:
  SuperFoo() : Foo("foo") {}
} ;

You can add commands simply by instantiating them:

Code: [Select]
class SuperFoo ...
{
...
} ;
static SuperFoo cmdSuperFoo ;

Trouble with this is its kinda advanced, its kinda tricky, and it relies on magic inner-workings, which means it doesn't come recommended for customer-facing product critical work, because it has an "orange" maintainability state, but if this is for some internal-only dev tool, it can save you a lot of unnecessary work.

I dunno how usable the sample are, they may be functional but I just typed them out as samples. If you're unclear on what unary_function and operator() do, Google Is Your Friend


OvermindDL1

  • Anti-Spam Moderator
  • Hero Member
  • ****
  • Posts: 855
  • Karma: 40
  • Programmer
    • View Profile
    • OvermindDL1's Site
Re: Pointers to class functions
« Reply #6 on: April 18, 2006, 06:06:19 PM »
Okay, does no one here really use Boost?

boost::function provides all this and due to heavy templating and specialization (boost::function and boost::bind as well as a few other boost libraries have already been accepted into the standard set for the next version of C++ in a few years) it generates the smallest assembly code possible.  It can link member pointers, even instances member pointers with boost::bind's help, normal pointers, functors, etc... etc... all in one object.  Not to mention Boost's license is about one of the easiest to use in the industry (since they are essentially a testbed for new C++ features).

Use this, and enjoy it. :)

HellishER

  • Guest
Re: Pointers to class functions
« Reply #7 on: April 30, 2006, 04:34:28 PM »
Thanks for your feedback.
Seams that i stick to boost.