Inserting into a map of member function pointers

Go To StackoverFlow.com

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.

2012-04-04 21:40
by FatalCatharsis
Can you give a little background? What is your goal in setting up your class this way - jpm 2012-04-04 21:42
Did you test it at all - Platinum Azure 2012-04-04 21:42
Use std::make_pair("func1", func1), it'll deduce the types for you - GManNickG 2012-04-04 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 2012-04-04 21:59


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);
2012-04-04 21:47
by Robᵩ
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 2012-04-04 22:02
@FatalCatharsis: Yes, it creates a default-initialized value. (In this case, a null pointer. - GManNickG 2012-04-04 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 : - FatalCatharsis 2012-04-04 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ᵩ 2012-04-05 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); }
}
2012-04-04 21:49
by Kerrek SB


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);
2012-04-04 21:48
by Adam Rosenfield
Ads