Я должен реализовать std::map
с <std::string, fn_ptr>
пары. Указатели функции являются указателями на методы того же класса, который владеет картой. Идея состоит в том, чтобы иметь прямой доступ к методам вместо того, чтобы реализовать переключатель или эквивалент.
(Я использую std::string
как ключи для карты)
Я довольно плохо знаком с C++, таким образом, кто-либо мог отправить некоторый псевдокод или ссылку, которая говорит о реализации карты с указателями функции? (указатели на методы, принадлежавшие тому же классу, который владеет картой),
Если Вы думаете, что существует лучший подход к моей проблеме, предложения также приветствуются.
Это самое простое, что я могу придумать. Обратите внимание на отсутствие проверки ошибок, и карту, вероятно, можно было бы сделать статической.
#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;
}
Реализация шаблона может выглядеть так:
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
; потенциальные сценарии связаны с безопасностью, но программист приложения может вывести приложение из строя без использования вашего класса.
newA
; потенциальные сценарии связаны с безопасностью, но программист приложения может вывести приложение из строя без использования вашего класса. m трудно представить себе случай, когда кто-то намеренно вызовет сбой в newA
; потенциальные сценарии связаны с безопасностью, но программист приложения может вывести приложение из строя без использования вашего класса. Другой вариант - использовать делегаты вместо указателей на функции. Эта реализация делегата довольно быстра, поддерживает полиморфизмы и хорошо работает с контейнерами 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;
};