std :: карта указателей на функции-члены? - PullRequest
18 голосов
/ 18 декабря 2009

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

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

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

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

Ответы [ 4 ]

28 голосов
/ 18 декабря 2009

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

#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;
}
3 голосов
/ 18 декабря 2009

Реализация шаблона может выглядеть следующим образом:

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 * в качестве параметра шаблона, но это не гарантирует работу на всех компиляторах.

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

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

1 голос
/ 24 сентября 2016

Начиная с C ++ 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{});
}
1 голос
/ 18 декабря 2009

Другой вариант - использовать делегаты вместо указателей на функции. Эта реализация делегата довольно быстрая, поддерживает полиморфизмы и хорошо работает с контейнерами 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;    
};      
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...