Ищете самый элегантный диспетчер кода - PullRequest
7 голосов
/ 19 августа 2009

Я думаю, что проблема довольно распространенная. У вас есть входная строка, и вам нужно вызывать функцию в зависимости от содержимого строки. Что-то вроде switch () для строк. Подумайте о параметрах командной строки.

В настоящее время я использую:

using std::string;

void Myclass::dispatch(string cmd, string args) {
    if (cmd == "foo")
        cmd_foo(args);
    else if (cmd == "bar")
        cmd_bar(args);
    else if ...
        ...
    else
       cmd_default(args);
}

void Myclass::cmd_foo(string args) {
...
}

void Myclass::cmd_bar(string args) {
...
}

и в шапке

class Myclass {
    void cmd_bar(string args);
    void cmd_foo(string args);
}

Так что каждый фу и бар мне приходится повторять четыре (4!) Раза. Я знаю, что могу передать указатели на функции и строки в статический массив и выполнять диспетчеризацию в цикле, сохраняя некоторые строки if ... else. Но есть ли какая-то хитрость макросов (или злоупотребление препроцессором, в зависимости от POV), которая позволяет каким-то образом определить функцию и в то же время автоматически обновлять массив? Так что мне пришлось бы написать это только дважды, или, возможно, один раз, если бы он использовался inline?

Я ищу решение на C или C ++.

Ответы [ 5 ]

8 голосов
/ 19 августа 2009

Звучит так, как будто вы ищете шаблон команды

Примерно так:

Создайте карту, подобную этой

std::map<std::string, Command*> myMap;

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

std::map<std::string, Command*>::iterator it = myMap.find(str);
if( it != myMap.end() ) {
    it->second->execute()
}

Чтобы зарегистрировать ваши команды, вы просто делаете это

myMap["foo"] = new CommandFoo("someArgument");
myMap["bar"] = new CommandBar("anotherArgument");
5 голосов
/ 19 августа 2009

Основное решение, согласно моей ссылке в комментарии к вопросу, состоит в том, чтобы сопоставить строку с каким-либо вызовом функции.

Чтобы зарегистрировать строку -> пара указатель функции / функтор:

Во-первых, у вас есть объект-диспетчер (шок! Ужас!). Давайте назовем это TheDispatcher - это обертка для map<string,Func>, где Func - это указатель вашей функции или тип функтора.

Затем зарегистрируйте класс:

struct Register {
   Register( comst string & s, Func f ) {
      TheDispatcher.Add( s, f );
   }
};

Теперь в ваших отдельных единицах компиляции вы создаете статические объекты (шок! ужас!):

Register r1_( "hello", DoSayHello );

Эти объекты будут созданы (при условии, что код не находится в статической библиотеке) и будут автоматически зарегистрированы в TheDispatcher.

А во время выполнения вы просматриваете строки в TheDispatcher и выполняете связанную функцию / функтор.

2 голосов
/ 19 августа 2009

в качестве альтернативы шаблону команды вы можете создать хеш-таблицу из строки -> указателей на функции :

typedef void (*cmd)(string);
1 голос
/ 20 августа 2009

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

1 голос
/ 19 августа 2009

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

Mappings.h:

// Note: no fileguard
// The first is  the text string of the command, 
// the second is the function to be called, 
// the third is the description.
UGLY_SUCKER( "foo", cmd_foo, "Utilize foo." );
UGLY_SUCKER( "bar", cmd_bar, "Turn on bar." );

parser.h:

class Myclass {
...
protected:
    // The command functions
    #define UGLY_SUCKER( a, b, c ) void b( args )
    #include Mappings.h
    #undef UGLY_SUCKER
};

Parser.cpp:

void Myclass::dispatch(string cmd, string args) {
    if (cmd == "")
        // handle empty case
#define UGLY_SUCKER( a, b, c ) else if (cmd == a) b( args )
#include Mappings.h
#undef UGLY_SUCKER
    else
       cmd_default(args);
}

void Myclass::printOptions() {
#define UGLY_SUCKER( a, b, c ) std::cout << a << \t << c << std::endl
#include Mappings.h
#undef UGLY_SUCKER
}

void Myclass::cmd_foo(string args) {
...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...