3

I'm doing a little game in C++ and I'm discovering the class members function pointers. I don't have any idea to make them work in the right way, but here is my attempt.

// A struct where the function pointer will be stored for the call
// By the way, is there a way to do the same thing with classes ?
// or are structs still fine in C++ ? (Feels like using char instead of string)
typedef struct      s_dEntitySpawn
{
  std::string       name;
  void          (dEntity::*ptr)();
} t_dEntitySpawn;

// Filling the struct, if the entity's classname is "actor_basicnpc",
// then I would like to do a call like ent->spawnBasicNPC
t_dEntitySpawn      dEntitySpawns[] = {
  { "actor_basicnpc", &dEntity::spawnBasicNPC },
  { 0, 0 }
};

// This is where each entity is analyzed
// and where I call the function pointer
void            dEntitiesManager::spawnEntities()
{
  dEntity       *ent;
  t_dEntitySpawn    *spawn;

    [...]

      // It makes an error here, feels very weird for me
      if (!spawn->name.compare(ent->getClassname()))
        ent->*spawn.ptr();

    [...]
}

Could you please give me a good advice about the right way to implement them ?

Best regards.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
Axel Isouard
  • 1,498
  • 1
  • 24
  • 38
  • 1
    Use interfaces instead. i.e. create a base class with a virtual method, then create the various classes with the appropriate implementation of that method. Makes the code easier to read and behave as you require. – Ed Heal Feb 08 '12 at 06:09
  • Exact duplicate of [How to invoke pointer to member function when it's a class data member?](http://stackoverflow.com/questions/6316751/how-to-invoke-pointer-to-member-function-when-its-a-class-data-member) – iammilind Feb 08 '12 at 06:15
  • 1
    I'll highly suggest you to use good naming style so that other can easily understand you code – Mr.Anubis Feb 08 '12 at 12:41

2 Answers2

5

I think that the line you're looking for is

(ent->*(spawn->ptr))();

Let's dissect this. First, we need to get to the actual member function pointer, which is

spawn->ptr

Since, here, spawn is a pointer, and we have to use -> to select the ptr field.

Once we have that, we need to use the pointer-to-member-selection operator to tell ent to select the appropriate member function:

ent->*(spawn->ptr)

Finally, to call the function, we need to tell C++ to invoke this member function. Due to operator precedence issues in C++, you first have to parenthesize the entire expression that evaluates to the member function, so we have

(ent->*(spawn->ptr))();

For what it's worth, this is one of the weirdest lines of C++ code that I've seen in a while. :-)

On a totally unrelated note, because you're using C++, I would avoid using typedef struct. Just say

struct t_dEntitySpawn {
  std::string name;
  void (dEntity::*ptr)();
};

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
3

The right way to program in this case is to stop programming like C in C++ and start using C++ features like virtual functions. :-P

I say "programming like C" because what you're doing resembles how C programmers implement polymorphism in the C language. There's no need to do that in C++ because C++ comes with built-in support for polymorphism, which was designed to help solve your situation. Virtual functions are how C++ implements polymorphism.

Not to mention that in this case, polymorphism via function pointers can be much faster than what you have now since string comparison is not required for C++ virtual functions to work.

There are use cases for member function pointers. This situation is not one of them. Especially since it obscures the intent of your code. (Remember, code is there for humans to read!)

class EntitySpawn 
{
public:
    void spawn_entity()
    {
        spawn();
    }

private:
    // std::string name; // Not necessary for polymorphism to work
    virtual void spawn() = 0;
};

class ActorBasicNPC : public EntitySpawn
{
private:
    virtual void spawn() { /* implementation specific to ActorBasicNPC */ }
};

void test_spawn(EntitySpawn& es)
{
    // This will call the correct implementation of spawn(),
    // even though we only got a reference to an EntitySpawn.
    es.spawn_entity();
}

int main()
{
    ActorBasicNPC npc;
    // Calls ActorBasicNPC::spawn() even though only a
    // reference to EntitySpawn was passed.
    test_spawn(npc);
};
In silico
  • 51,091
  • 10
  • 150
  • 143
  • Thank you very much, I clearly see it as a better way for organizing this, I've completely forgot about virtuals and I feel that it fits better for the C++ language. – Axel Isouard Feb 08 '12 at 06:17
  • +1 right. We're pretty much following a factory pattern. You must have an instance to the factory (i.e. any instance that implements EntitySpawn) prior to being able to use the factory. There's no harm in managing a list of factories to spawn a list of entities. – Stephen Quan Feb 08 '12 at 06:20
  • While this is probably much simple for OP's use case, virtual functions can't replace member function pointers for many tasks. With a virtual function, a single object can only be registered for one specific callback. In contrast, if storing object pointers and a function to call, you can register calls to different functions for a single object. Basides, if that were useless, there would be no use for `boost::function<>` and the newer `std::function<>`. – André Caron Feb 08 '12 at 06:21
  • @André Caron: Nowhere in my post did I say that member function pointers were useless. Especially since [one of my previous answers depend on them](http://stackoverflow.com/questions/4298408/5-years-later-is-there-something-better-than-the-fastest-possible-c-delegate/4300544#4300544). :-) But you bring up a fair point. – In silico Feb 08 '12 at 06:24
  • I've just skimmed over your post and it seems like an interesting read. I've spent some time wrapping a lot of native Win32 API objects and used a neat technique to hide the painful callback signature. [Check out what it looks like](https://gist.github.com/1766094) and tell me if you notice any similar patterns :-) – André Caron Feb 08 '12 at 06:44
  • @André Caron: You know what, I do notice a similar pattern. :-) – In silico Feb 08 '12 at 12:29