3

My question is a little complicated so I'll start with an example:

class a
{
public:
     a()
     {
          pointerMap.insert(pair<std::string, void a::*(int, int)> ("func1", func1);
          pointerMap.insert(pair<std::string, void a::*(int, int)> ("func2", func2);
     }

private:
     void func1(int a, int b);
     void func2(int a, int b);
     std::map<std::string, void a::* (int, int)> pointerMap;
}

My question is, is this the right way to go about adding pointers to member functions to a map within an object, so that you are only referencing the individual instance's func1 or func2?

And also, I have no clue how I would go about calling this function from the pointer. Would it be something like this?

map["func1"](2,4);

I'm a little confused about the syntax when working with member functions.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
FatalCatharsis
  • 3,407
  • 5
  • 44
  • 74
  • Can you give a little background? What is your goal in setting up your class this way? – jpm Apr 04 '12 at 21:42
  • Use `std::make_pair("func1", func1)`, it'll deduce the types for you. – GManNickG Apr 04 '12 at 21:44
  • This is literally just a little example i doodled up to simplify my problem down to just the concept. i'm actually trying to make my own little win32 wrapper that's object oriented. This is for installing message handlers where the index is the msg (i know it's actually a uint, just used a string as example) and what's stored is the member function pointer to a function handler you have written. when a message is received, it sees if a message of the same type was stored in the map, and calls it's handler. you will never access these outside the instance. – FatalCatharsis Apr 04 '12 at 21:59

3 Answers3

7

First, the code:

#include <map>
#include <string>
class a
{
public:
     a()
     {
       pointerMap["func1"] = &a::func1;
       pointerMap["func2"] = &a::func2;
     }

     void invoke(const std::string& name, int x, int y) {
       if(pointerMap[name])
         (this->*pointerMap[name])(x, y);
     }

private:
     void func1(int a, int b) {};
     void func2(int a, int b) {};
     std::map<std::string, void (a::*)(int, int)> pointerMap;
};

int main () {
  a o;
  o.invoke("func1", 1, 2);
}

Now, for your questions:

my question is, is this the right way to go about adding pointers to member functions to a map within an object

I find the subscript operator [] much easier to read than the insert that you were doing.

so that you are only referencing the individual instance's func1 or func2.

Pointer-to-member-function doesn't have an instance associated with it. You bind the pointer to an instance when you invoke it. So, your map could have been a static member just as easily.

how i would go about calling this function from the pointer.

The syntax is either: (instance.*pointer)(args) or (class_pointer->*pointer)(args). Since you didn't say what instance the functions should be invoked on, I assumed this. Your pointers live in the map, so we have:

((this)->*(this->pointerMap["func1"]))(arg1, arg2)

or

(this->*pointerMap[name])(x, y);
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • wow, this is very thorough good sir. I was not aware that using the subscript operator without having inserted the index previously would not cause an out of bounds error. So using the subscript operator for an index that doesn't exist will create an index for you in the map? – FatalCatharsis Apr 04 '12 at 22:02
  • @FatalCatharsis: Yes, it creates a default-initialized value. (In this case, a null pointer.) – GManNickG Apr 04 '12 at 22:15
  • yeh, that's pretty cool. so now all i need to make sure to do is verify that a map index with that name exists before accessing it, and check for a null function pointer by chance, and i'm set. thanks for answering my questions and more :P – FatalCatharsis Apr 04 '12 at 22:18
  • @FatalCatharsis - I added an `if` to the `invoke()` method for you. Using just it will do both of the things you ask for. – Robᵩ Apr 05 '12 at 02:24
2

It's sort-of right. Maybe a typedef can make things a bit cleaner:

typedef std::map<std::string, void(a::*)(int, int)> pfmap_type;
pfmap_type m;               //    ^^^^^^

// ...

m.insert(pfmap_type::value_type("hello", &a::func1));
                                      // ^^^

(this->*(m["hello"]))(1, 2);
(this->*(m.find("hello")->second))(3, 4);

Actually, neither of the map accesses are a good idea, because you absolutely must check that the map item exists, for otherwise you have a bad pointer. So I recommend something like this:

void call(const char * key, int a, int b) const
{
    pfmap_type::const_iterator it = m.find(key);
    if (it != m.end()) { (this->*(it->second))(a, b); }
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

That's the correct way of inserting the pointers into the map, but you can tidy things up a little by using make_pair, which deduces the template arguments for you:

pointerMap.insert(std::make_pair("func1", &func1));  // The '&' is optional

To call a function, you need to use the .* or ->* operator, depending on whether the object you're calling it on is being referenced through a pointer or not:

a obj;  // Regular object
(a .* map["func1")(2, 4);  // Extra parens needed due to operator precedence

a *ptr;  // Pointer
(a ->* map["func1")(2, 4);

Some people like to define a macro to make it more obvious what you're doing, since the syntax can be a little confusing to some:

#define CALL_MEMBER_FUN_PTR(obj, fun) ((obj) ->* (fun))
...
a *ptr;
CALL_MEMBER_FUN_PTR(ptr, map["func1"])(2, 4);
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589