Выбор функции-члена на основе строки

У меня есть набор классов аппаратных обработчиков, все они получены из базового класса, которые должны отвечать на входящий пакет данных. Часть этого пакета представляет собой строку ASCII, которая определяет, какая функция-член аппаратного обработчика используется для обработки пакета (например, «вентилятор» будет выполнять ToggleFan() функция.

class HardwareHandler {
virtual void dispatchCommand(const String& cmd) = 0;
}

class FooblerHandler : public HardwareHandler {

void toogleFan();

void dispatchCommand(const String& cmd) {
//is this a "good" way to do this?
if (cmd == "fan")
toggleFan();
}
}

Я использую JUCE в качестве основы, что означает, что у меня есть такие вещи, как шаблон HashMapс и String,

Тем не менее, я не могу придумать аккуратный способ выбора правильной функции-обработчика на основе этой строки. Конструкция

if (str == "hello")
FooCommand();
else if (str == "bar")
BarCommand();

Концептуально это выглядит мне некрасиво, поскольку в нем много сравнительно дорогих сравнений строк. Тем не менее, код легко написать, и логика хранится в одном месте для каждого класса.

Другая альтернатива, которую я попробовал, состоит в том, чтобы создать хэш-карту строк для перечисления и использовать это выражение switch:

switch (str.getHash())
{
case CmdFoo:
FooCommnad();
break;
....and so on
}

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

Еще кое-что, что я попробовал, это хэш-карта, формирующая строку для самого указателя на функцию-член, в надежде, что она сможет перейти непосредственно из строки в функцию-член, не перечисляя их в операторе case, а также позволяет использовать очень общую функцию диспетчеризации. , поскольку он просто должен искать в хэш-карте, ему даже не нужно знать все опции — они могут содержаться исключительно в хэш-карте, что позволяет мне, возможно, вставить функцию диспетчеризации в базовый класс обработчика. и не повторяться в каждом конкретном устройстве обработчик. Тем не менее, этот метод поставил меня в тупик, так как я не могу понять, как это сделать правильно, или даже если это возможно сделать со статической хэш-картой и функциями-членами.

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

1

Решение

Вот моя попытка. Вы можете инкапсулировать механизм отображения в класс:

#include <iostream>
#include <string>
#include <functional>
#include <map>

class X;

template<class X>
class handler_factory;

template<>
class handler_factory<X>
{
private:
using HandlerType = void (X::*)();
public:
handler_factory();

HandlerType get(const std::string& name) const
{
if (handlers.find(name) == handlers.end())
return nullptr;
else
return (*handlers.find(name)).second;
}
private:
std::map<std::string, HandlerType> handlers;
};

class X
{
public:
friend class handler_factory<X>;
private:
void f();
void h();
};

handler_factory<X>::handler_factory()
{
handlers["f"] = &X::f;
handlers["h"] = &X::h;
}

void X::f() { std::cout << "X::f();"; }
void X::h() { std::cout << "X::h();"; }

И ваш способ отправки может быть реализован как:

void dispatch_method(const std::string& name)
{
if (find_handler(name))
(this->*find_handler(name))();
}

int main()
{
X().dispatch_method("f");
}

куда find_handler определяется как частный вспомогательный метод:

private:
auto find_handler(const std::string& name)
-> decltype(handler_factory<X>().get(name))
{
return handler_factory<X>().get(name);
}
1

Другие решения

Я думаю, что самый эффективный способ справиться с проблемой — это создать std::map это отобразит ваши строки в соответствующие функции. Метод быстрый (благодаря алгоритму логарифмического поиска), простой и безопасный.

class FooblerHandler : public HardwareHandler {
typedef void (HardwareHandler::*function)();
map<string,function> commandMap;

void dispatchCommand(const string& cmd) {
if(commandMap.count(cmd))
(this->*commandMap.find(cmd)->second)();
else
cout << "No command found with name \"" <<cmd<< "\"." << endl;
}
};

И, конечно, вы должны инициализировать карту внутри конструктора (или где-то еще до его использования):

commandMap["fan"] = &FooblerHandler::toogleFan;
commandMap["someOtherCommand"] = &FooblerHandler::otherFunction;

Карты и включен в Стандартную библиотеку шаблонов (STL), которая поставляется практически всеми IDE.

РЕДАКТИРОВАТЬ:
Я не совсем прочитал текст в конце. Хорошо — теперь вы знаете синтаксис 🙂

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector