Используйте typedef в структуре для именования и индексации текстовых команд - PullRequest
2 голосов
/ 01 ноября 2011

Я работаю с простым приложением командной строки, которое принимает текст ASCI и интерпретирует его как команду.

Я попытался минимизировать избыточность в этом приложении на примере http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html.

например: Рассмотрим программу на C, которая интерпретирует именованные команды. Вероятно, должна быть таблица команд, возможно, массив структур, объявленных следующим образом:

 struct command
 {
   char *name;
   void (*function) (void);
 };

 struct command commands[] =
 {
   { "quit", quit_command },
   { "help", help_command },
   ...
 };

Было бы лучше не указывать имя каждой команды дважды, один раз в строковой константе и один раз в имени функции. Макрос, который принимает имя команды в качестве аргумента, может сделать это ненужным. Строковую константу можно создать с помощью строкового преобразования, а имя функции - путем объединения аргумента с помощью `_command '. Вот как это делается:

 #define COMMAND(NAME)  { #NAME, NAME ## _command }

 struct command commands[] =
 {
   COMMAND (quit),
   COMMAND (help),
   ...
 };

Теперь предположим, что я хочу иметь командную строку и значение индекса (то есть: int), а не строку и указатель на функцию.

 struct command
 {
   char *name;
   int command_idx;
 };

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

Я могу вручную создать перечислимый тип данных, но затем я определил перечислимые константы в отдельном операторе перечисления. IE: команды enum {cmd_quit = 0, cmd_help} и, в конце концов, мне все равно приходится вводить каждое имя команды дважды: один раз с помощью макроса COMMAND () и снова в моем перечислении.

Существует ли какой-либо метод, использующий препроцессор C, который позволил бы мне создать макрос, создает структуру "command" (с элементами string и int) и автоматически нумерует значение int (command_idx), когда я добавляю больше команд через КОМАНДА () макрос?

Мне также известно, что я могу просто использовать вызовы strcmp () для каждой возможной команды и сравнивать с вводом, предоставленным пользователем, но я хотел бы иметь прямое средство индексации команд с помощью значения command_idx, так как в отличие от strcmp'а против массивного списка команд каждый раз (то есть: O (1) вместо O (n)). Я также хочу избежать любой необходимости набирать имя команды более одного раза.

Спасибо!

Ответы [ 2 ]

5 голосов
/ 01 ноября 2011

Вы можете использовать переопределение макросов для достижения этой цели. Сначала вы создаете файл, который просто перечисляет ваши команды с именем commands.inc:

COMMAND(quit)
COMMAND(help)
...

Затем в вашем C-источнике вы можете #include "commands.inc" несколько раз, с разными определениями COMMAND(), контролировать его работу. Например:

struct command
{
   char *name;
   int command_idx;
};

#define COMMAND(NAME) CMD_ ## NAME,

enum command_enum {
#include "commands.inc"
};

#undef COMMAND

#define COMMAND(NAME) { #NAME, CMD_ ## NAME },

struct command commands[] =
{
#include "commands.inc"
};

#undef COMMAND

(Обратите внимание, что этот конкретный пример основан на улучшении C99, которое позволяет завершать , в конце списков в объявлении enum и составном инициализаторе - вы можете легко обойти это в C89, добавив фиктивную запись в конце).

2 голосов
/ 01 ноября 2011

Вопрос:

Существует ли какой-либо метод, использующий препроцессор C, который позволил бы мне создать макрос, создающий структуру "command" (с элементами string и int), и автоматически нумерующий intзначение (command_idx), как я добавляю больше команд через макрос COMMAND ()?

Да, и поскольку вы пометили вопрос также как C ++:

#include <iostream>
#include <map>
#include <string>
using namespace std;

map< string, int >     commands;

bool register_cmd( int id, string const& name )
{
    commands[name] = id;
    return true;
}

#define COMMAND( name ) \
    int const name ## _cmd = __LINE__; \
    bool const name ## _reg = register_cmd( name ## _cmd, #name )

COMMAND( exit );
COMMAND( help );
COMMAND( do_stuff );

int cmd_id( string const& name )
{
    auto const it = commands.find( name );
    return (it == commands.end()? -1 : it->second );
}

int main()
{
    for( auto it = commands.begin();  it != commands.end();  ++it )
    {
        cout << it->first << " => " << it->second << endl;
    }

    cout << "Gimme a command, please: ";
    string cmd;  getline( cin, cmd );
    switch( cmd_id( cmd ) )
    {
    case exit_cmd:
        cout << "You typed an EXIT command, which has id " << exit_cmd << endl;
        break;
    default:
        cout << "Hey, why not try an 'exit' command?" << endl;
    }
}

Я только что использовалmap вместо необычной новой хэш-таблицы C ++ 11, потому что map работает со старыми компиляторами и здесь нет реальной необходимости сокращать наносекунды.

Cheers & hth.,

...