Std:: map указателей функций-членов?

Мне нужно реализовать пары std::map с <std::string, fn_ptr>. Указатели функций являются указателями на методы того же класса, которым принадлежит карта. Идея состоит в том, чтобы иметь прямой доступ к этим методам вместо реализации коммутатора или эквивалента.

(Я использую std::string в качестве ключей для карты)

Я новичок в С++, так может ли кто-нибудь опубликовать псевдокод или ссылку, которая говорит о реализации карты с указателями функций? (указатели на методы, принадлежащие тому же классу, которому принадлежит карта)

Если вы считаете, что есть лучший подход к моей проблеме, предложения также приветствуются.

Ответы

Ответ 1

Это про самый простой, который я могу придумать. Обратите внимание на отсутствие проверки ошибок, и, вероятно, карта может быть сделана статической.

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

Ответ 2

Реализация шаблона может выглядеть так:

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 */
    ...
}
....

Это требует, чтобы значение, используемое для определения того, какой newA вызывается, должно быть известно во время компиляции. Вы могли бы использовать параметр const char * в качестве параметра шаблона, но он не гарантированно работает на всех компиляторах.

Еще один вариант - создать вспомогательные фабрики, по одному для каждого метода создания factory, и сохранить их на карте. Это не огромное преимущество перед хранением указателей методов, но позволяет вам определить метод создания по умолчанию и упростить выборку из карты (нет необходимости проверять, существует ли ключ, потому что вы получите по умолчанию factory), С другой стороны, на карту будет добавлена ​​запись для каждого неизвестного ключа.

Кроме того, если вы используете enum, а не строку для типа ключа, вам не нужно беспокоиться о проверке наличия ключа на карте. Хотя для кого-то можно передать недействительный ключ enum на newA, им придется явно указать аргумент, а это значит, что они не сделают это случайно. Мне трудно представить себе случай, когда кто-то будет целенаправленно вызывать сбой в newA; потенциальные сценарии связаны с безопасностью, но программа-программист может сбой приложения без использования вашего класса.

Ответ 3

Другой вариант - использовать делегаты в качестве противопоставления указателям на функции. Эта реализация делегата довольно быстро, поддерживает полиморфизмы и хорошо работает с stl-контейнерами. У вас может быть что-то вроде:

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

Ответ 4

Так как С++ 14, мы можем использовать общую лямбду, чтобы легко избавиться от указателей на методы-члены.
Это следует за минимальным рабочим примером прямой функции, состоящей из общей лямбда-функции:

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