tags:

views:

415

answers:

4

I need to implement an std::map with pairs. The function pointers are pointers to methods of the same class that owns the map. The idea is to have direct access to the methods instead of implementing a switch or an equivalent.

( I am using std::string as keys for the map )

I'm quite new to C++, so could anyone post some pseudo-code or link that talks about implementing a map with function pointers? ( pointers to methods owned by the same class that owns the map )

If you think there's a better approach to my problem, suggestions are also welcome.

Thanks

A: 

Set up a typedef for the function pointer, then declare an std::map<*key type, function pointer type*>.

An alternative approach may be to use function objects, (a.k.a. functors, or functoids). Use a map of functors.

By the way, what are you using as the key for the map?

Thomas Matthews
I edited the post to point out that I'm using std::string as keys for the map, sorry for not pointing out earlier.
Mr.Gando
+4  A: 

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;
}
anon
Worked nicely, thanks!, this thread became pretty useful, since the template Implementation posted by @outis, is really good too. Will mark this as the answer to the specific question, but be sure to read that one too.
Mr.Gando
+1  A: 

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.

outis
Am I missing something or does tnot he code above only work for compile-time values of th enumerated type? If I have an enumeration variable of type "which", I can't create new A thingies depending on the contents of the variable.
anon
@Neil: You're missing nothing. That's the big limitation of the template approach. It may not be suitable for Mr. Gando's purposes, but it's worth considering.
outis
In that case I don't see the advantage of your approach over named functions.
anon
Using type-correctness to ensure valid method invocation is a good thing, because everything but the invocation is handled at compile time. Advantages: O(1) dispatch time at runtime and no real error checking needs to be done (the default `Factory::newA` shouldn't actually be callable, unless the coder forgets to implement a specialization, which should be caught by a unit test). The dynamic approach of doing everything at runtime has it's own advantages; I mostly posted the sample because Mr. Gando asked to see code for rlbond's suggestion.
outis
+1  A: 

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