Генерация массива препроцессора

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

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

Немного почитав, я взглянул на препроцессор boost. К сожалению, похоже, что нет способа «сохранить» новый массив. Я хотел бы сделать что-то вроде этого:

#define SOME_ARRAY (0, ())
#define CONSOLE_COMMAND(a) \
  #redefine SOME_ARRAY BOOST_PP_ARRAY_PUSH_BACK(SOME_ARRAY, #a) \
  void a(some_arguments)

К сожалению, насколько мне известно, redefine не существует, и #define нельзя использовать в макросе (пожалуйста, поправьте меня, если я ошибаюсь).

Я взглянул на слоты прекомпилятора boost, но я не думаю, что смогу изменить переменную в нем после установки.

Есть ли способ сделать это, кроме написания собственного препроцессора? Не так ли, что было бы хорошим началом в обучении написанию одного? (Используя MinGW с Code::Blocks).


person DrBearhands    schedule 04.09.2012    source источник
comment
Я уверен, что есть простые в обслуживании способы решения вашей проблемы без использования препроцессора. Почему не описываете проблему?   -  person jrok    schedule 04.09.2012


Ответы (2)


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

Что-то вроде этого:

// functions.h
#ifndef FUNCTION_H_
#define FUNCTION_H_

#ifndef FUNCTION
# define FUNCTION(name) \
    void name(const std::vector<std::string> &);
#endif

FUNCTION(foo)
FUNCTION(bar)

#endif

// functions.cpp
// File that defines the function table

#include <functional>

using function_type = std::function<void(const std::vector<std::string> &)>;

#define FUNCTION(name) \
    { #name, name },

std::map<std::string, function_type> functions = {
#include "functions.h
};

Теперь у вас есть std::map, содержащий указатели на функции, проиндексированные по имени функции.

Да, вам по-прежнему нужно поддерживать «список» ваших функций, а именно список прототипов в functions.h, но когда вы добавляете (или удаляете) функции, очень легко «модифицировать» этот список.

person Some programmer dude    schedule 04.09.2012

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

Одним из возможных решений было бы использование переопределения и самостоятельного повторного включения в качестве метода X-Macros:

#define CONSOLE_COMMAND(a,body) \
    void a(some_arguments) body

CONSOLE_COMMAND(my_command, { ... })

const char *array[] = {
#undef CONSOLE_COMMAND
#define CONSOLE_COMMAND(a,body) #a ,
#include __FILE__
};

Более идиоматичное решение C++ состояло бы в том, чтобы ваши команды были объектами файловой области, конструкторы которых регистрируют их во время запуска программы.

person ecatmur    schedule 04.09.2012
comment
Итак, если я правильно понимаю, это та же концепция, что и решение Иоахима Пилеборга: измените определение и повторно включите в массив, чтобы теперь он стал желаемыми записями. Я действительно не понимаю, для чего нужны COMMAND_NAME и COMMAND_DEFINITION. - person DrBearhands; 04.09.2012
comment
@DrBearhands ой, не обращайте внимания. - person ecatmur; 04.09.2012