Как я могу сгенерировать список через препроцессор C (cpp)?

Я хотел бы сделать что-то вроде следующего:

F_BEGIN

F(f1) {some code}
F(f2) {some code}
...
F(fn) {some code}

F_END

и заставить его генерировать следующее

int f1() {some code}
int f2() {some code}
...
int fn() {some code}

int (*function_table)(void)[] = { f1, f2, ..., fn };

Сами функции просты. То, что я не могу сделать, это отслеживать все имена до конца для function_table.

Я посмотрел на этот вопрос и этот вопрос, но я не мог заставить что-то работать на меня. Есть идеи?

4 ответа

Решение

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

#define FUNCTION_TABLE(F) \
    F(f1, { some code }) \
    F(f2, { some code }) \
    F(f3, { some code }) \
:

    F(f99, { some code }) \
    F(f100, { some code })

#define DEFINE_FUNCTIONS(NAME, CODE)     int NAME() CODE
#define FUNCTION_NAME_LIST(NAME, CODE)   NAME,

FUNCTION_TABLE(DEFINE_FUNCTIONS)
int (*function_table)(void)[] = { FUNCTION_TABLE(FUNCTION_NAME_LIST) };

Если у вас есть компилятор C99, препроцессор имеет списки аргументов переменной длины. P99 имеет препроцессор P99_FOR это может сделать "развертывание кода", как тот, который вы хотите достичь. Чтобы оставаться рядом с вашим примером

#define MYFUNC(DUMMY, FN, I) int FN(void) { return I; } 
#define GENFUNCS(...)                                          \
P99_FOR(, P99_NARG(__VA_ARGS__), P00_IGN, MYFUNC, __VA_ARGS__) \
int (*function_table)(void)[] = { __VA_ARGS__ }

GENFUNCS(toto, hui, gogo);

будет расширяться до следующего (не проверено)

int toto(void) { return 0; } 
int hui(void) { return 1; }
int gogo(void) { return 2; }
int (*function_table)(void)[] = { toto, hui, gogo };

Есть такая штука под названием X Macro, которая используется как:

метод для надежного ведения параллельных списков, кода или данных, соответствующие элементы которых должны располагаться в том же порядке

Вот как это работает:

    #include <stdio.h>

//you create macro that contains your values and place them in (yet) not defined macro
#define COLORS\
    X(red, 91)\
    X(green, 92)\
    X(blue, 94)\

//you can name that macro however you like but conventional way is just an "X"

//and then you will be able to define a format for your values in that macro
#define X(name, value) name = value,
typedef enum { COLORS } Color;
#undef X //just another convention (so you don't use it accidentally, I think)

int main(void)
{
    #define X(name, value) printf("%d, ", name);
    COLORS
    #undef X
    return 0;
}

Решением вашей проблемы будет:

#define FUNCTIONS \
F(f1, code1)\
F(f2, code2)\
F(f3, code3)

#define F(name, code) int name(void){code}
FUNCTIONS
#undef F


#define F(name, code) &name,
int (*function_table[])(void) = { FUNCTIONS };
#undef F

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

#define FUNCTIONS \
 foo(a,b,c,d) \
 foo(a,b,c,d) \
 foo(a,b,c,d)

now, 

#define foo(a,b,c,d) \
 a+b ;

FUNCTIONS

#undef foo

позже, когда вы хотите сделать что-то другое с тем же списком

#define foo(a,b,c,d) \
 a: c+d ;

FUNCTIONS

#undef foo

Это немного уродливо и громоздко, но это работает.

Boost - это библиотека C++, но модуль Preprocessor все еще должен быть пригоден для использования в C. Он предлагает некоторые удивительно продвинутые типы данных и функциональность для использования в препроцессоре. Вы можете проверить это.

Другие вопросы по тегам