Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::map of member function pointers?

Tags:

c++

stl

People also ask

How do you use pointers to member functions?

The pointer to member operators . * and ->* are used to bind a pointer to a member of a specific class object. Because the precedence of () (function call operator) is higher than . * and ->* , you must use parentheses to call the function pointed to by ptf .

Can we create a pointer to a member function?

You can use pointers to member functions in the same manner as pointers to functions. You can compare pointers to member functions, assign values to them, and use them to call member functions.

Do all member functions have a this pointer?

Every object in C++ has access to its own address through an important pointer called this pointer. The this pointer is an implicit parameter to all member functions.

What is the type of this pointer in its member function?

The this pointer is a pointer accessible only within the nonstatic member functions of a class , struct , or union type. It points to the object for which the member function is called. Static member functions don't have a this pointer.


This is about the simplest I can come up with. Note no error checking, and the map could probably usefully be made static.

#include <map>
#include <iostream>
#include <string>
using namespace std;

struct A {
    typedef int (A::*MFP)(int);
    std::map <string, MFP> fmap;

    int f( int x ) { return x + 1; }
    int g( int x ) { return x + 2; }


    A() {
        fmap.insert( std::make_pair( "f", &A::f ));
        fmap.insert( std::make_pair( "g", &A::g ));
    }

    int Call( const string & s, int x ) {
        MFP fp = fmap[s];
        return (this->*fp)(x);
    }
};

int main() {
    A a;
    cout << a.Call( "f", 0 ) << endl;
    cout << a.Call( "g", 0 ) << endl;
}

A template implementation could look like:

class Factory {
public:
    enum which {
        foo, bar, baz
    };

    template<which w>
    A* newA(...);
    ...
};
template<Factory::which w>
A* Factory::newA(...) {
    /* default implementation */
    throw invalid_argument();
}
template<>
A* Factory::newA<Factory::foo>(...) {
    /* specialization for a 'foo' style A */
    ...
}
....

This requires that the value used to determine which newA is called be known at compile time. You could potentially use a const char * as the template parameter, but it's not guaranteed to work on all compilers.

Yet another option is to create helper factories, one for each factory creation method, and store those in the map. This isn't a huge advantage over storing method pointers, but does let you define a default creation method and simplifies fetching things from the map (there's no need to check that the key exists, because you'll get a default factory). On the downside, an entry for each unknown key would be added to the map.

Also, if you use an enum rather than a string for the key type, you shouldn't need to worry about checking whether a key exists in the map. While it's possible for someone to pass an invalid enum key to newA, they'd have to explicitly cast the argument, which means they're not going to do it by accident. I'm having a hard time imagining a case where someone would purposefully cause a crash in newA; the potential scenarios involve security, but an application programmer could crash the app without using your class.


Since C++14, we can use a generic lambda to get rid easily of pointers to member methods.
It follows a minimal, working example of a forward function made up with a generic lambda function:

#include<utility>
#include<map>
#include<string>
#include<iostream>

struct SomeClass { };
struct SomeOtherClass { };

struct Test {
    void test(SomeClass) { std::cout << "SomeClass" << std::endl; }
    void test(SomeOtherClass) { std::cout << "SomeOtherClass" << std::endl; }
};

int main() {
    Test test;

    auto l = [&test](auto c){ test.test(c); };
    std::map<std::string, decltype(l)> m;

    m.emplace("foo", l);
    m.emplace("bar", l);

    m.at("foo")(SomeClass{});
    m.at("bar")(SomeOtherClass{});
}

Another option is to use delegates as oppose to function pointers. This delegate implementation is pretty fast, supports polymorphisms, and plays well with stl containers. You could have something like:

class MyClass {
public:
    // defines
    typedef fastdelegate::FastDelegate2<int, int, int> MyDelegate;
    typedef std::map<std::string, MyDelegate> MyMap;

    // populate your map of delegates
    MyClass() {
        _myMap["plus"] = fastdelegate::MakeDelegate(this, &Plus);
        _myMap["minus"] = fastdelegate::MakeDelegate(this, &Minus);
    }

    bool Do(const std::string& operation, int a, int b, int& res){
        MyMap::const_iterator it = _myMap.find(operation);
        if (it != _myMap.end()){
            res = it.second(a,b);
            return true;
        }

        return false; 
    }
private:
    int Plus (int a, int b) { return a+b; }
    int Minus(int a, int b) { return a-b; }
    MyMap _myMap;    
};