FatalCatharsis
FatalCatharsis

Reputation: 3567

Inserting into a map of member function pointers

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.

Upvotes: 3

Views: 3832

Answers (3)

Robᵩ
Robᵩ

Reputation: 168626

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);

Upvotes: 7

Kerrek SB
Kerrek SB

Reputation: 477010

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); }
}

Upvotes: 2

Adam Rosenfield
Adam Rosenfield

Reputation: 400224

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);

Upvotes: 1

Related Questions