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