Использование макросов C X в сочетании с #ifdef
Предполагая, что мой код выглядит следующим образом:
#ifdef COND1
extern int func1(void);
#endif
...
#ifdef CONDN
extern int funcn(void);
#endif
my_struct funcs[] = {
#ifdef COND1
{"func1 description", func1},
#endif
...
#ifdef CONDN
{"funcn description", funcn},
#endif
{NULL, NULL},
};
Можно ли заменить это макросами X, чтобы минимизировать повторение имен функций и условий в обеих частях?
Без #ifdef CONDX
условия, это кажется довольно прямым. Тем не менее, я понятия не имею, как включить их в макрос X, потому что не разрешено использовать #ifdef
в #define
,
5 ответов
Ключ, я думаю, состоит в том, чтобы "вычленить" условие. Например, COND1
(или COND2 и т. д.) должны окружать определение funcs
массив, а не наоборот. Здесь я предполагаю, что вы можете свести ваши условия к целочисленным определениям. Это может быть сделано, например,
#if COND1
# define N 1
#elif COND2
# define N 2
// ...
#endif
Давайте также предположим, что у вас есть несколько заглушек функций (не только func
) которые все расширены до stub<n>
похожие имена Затем вы можете полностью параметризовать генерацию имени вашей функции следующим образом. (Код использует конкатенацию строковых литералов и переменных цикла. gcc -std=c99
компилирует это нормально.)
Основной файл. Объявите функции и определите массив структуры description.
#include<stdio.h>
// example struct definition
typedef struct { const char* description; int (*f)(void); } my_struct;
// A two-stage macro expansion is necessary because
// macro parameters are taken literally when used in
// concatenation or stringification
// (cf. https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html)
// Expands to function declaration
#define X_LITERAL_PARAMS(fname, suffix) extern int fname ## suffix (void);
#define X(fname, suffix) X_LITERAL_PARAMS(fname, suffix) // this expands suffix e.g. to 1
// define extern int functions
#define N 1 // select which function variants
#include "xmacro_x.h"
#undef X_LITERAL_PARAMS
#define X_LITERAL_PARAMS(fname, suffix) { "Calling " #fname #suffix, fname ## suffix},
my_struct funcs[] = {
#undef N
#define N 1 // select which function variants
#include "xmacro_x.h"
// defines descriptions for functions
# include "xmacro_x.h"
};
// Print description and call each function
// in the struct array
int main(void)
{
for(int i=0; i<sizeof(funcs)/sizeof(my_struct); i++)
{
printf("%s yields %d\n\n", funcs[i].description, funcs[i].f());
}
return 0;
}
Файл funcs.c фактически определяет функции.
// Define functions named as in the struct array
// for proof of concept
#include <stdio.h>
// two-stage expansion again
#define X_LITERAL_PARAMS(f, n) \
int f ## n (void){ return printf("This is %s\n", #f #n);}
#define X(a,b) X_LITERAL_PARAMS(a,b)
#define N 1
#include "xmacro_x.h"
Наконец, довольно неинтересный файл xmacro_x.h предоставляет "макросы X", расширенные до различных фрагментов кода в исходных файлах. Здесь представлены различные "семейства функций", заглушки имен, которые будут объединены с числовыми суффиксами позже.
// The "X macros" which will be expanded to different things later
X(func, N)
X(gunc, N)
//...
Не уверен, что X макросы являются решением здесь. Вы можете, однако, использовать немного магии препроцессора, чтобы уменьшить количество печатаний. Проблема остается в условной компиляции (#ifdef
s) в вашем примере. Не зная, как выглядят эти условия, трудно уменьшить количество набираемых текстов.
Придумываю следующее:
#define D(n) extern int func ## n(void);
#define A(n) {"func" #n " description", func ## n},
#ifdef COND1
D(1)
#endif
#ifdef COND2
D(2)
#endif
my_struct funcs[] = {
#ifdef COND1
A(1)
#endif
#ifdef COND2
A(2)
#endif
};
Это, я думаю, шаг в том направлении, к которому вы стремитесь. Чтобы увидеть, что это делает, вы можете попробовать
gcc -E -DCOND1 <file-with-contents-above>.c
(если вы используете Unix) или
cl -E -DCOND1 <file-with-contents-above>.c
(если вы используете Windows, используя Visual Studio).
Предположим, вы хотите определить COND1, но не CONDN. Я думаю, что вы можете сделать следующее:
#define COND1(...) __VA_ARGS__
#define CONDN(...)
#define MY_XLIST \
X(COND1, func1, "func1 description") \
X(CONDN, funcn, "funcn description")
#define X(a, b, c) a(extern int b (void);)
MY_XLIST
#undef X
#define X(a, b, c) a({c, b},)
my_struct funcs[] = {
MY_XLIST
{NULL, NULL},
};
#undef X
Проблема в том, что вам нужно определить все макросы, но с разными расширениями.
Вы не можете использовать #ifdef
внутри #define
так что вы не можете использовать #define
на основе X-макросов, но вы все еще можете использовать #include
на основе X-макросов.
Например, используйте файл t.def
:
#ifdef COND1
F(func1, "func1 description")
#endif
#ifdef COND2
F(func2, "func2 description")
#endif
И в вашем основном файле:
#define COND2
#define F(name, desc) extern int name(void);
#include "t.def"
#undef F
mystruct funcs[] = {
#define F(name, desc) {desc, name},
#include "t.def"
#undef F
{NULL, NULL},
};
Это будет обработано для:
extern int func2(void);
mystruct funcs[] = {
{"func2 description", func2},
{NULL, NULL},
};
Ключ к использованию условной компиляции с макросами x (я называю их макросами списков) заключается в понимании того, что директивы препроцессора не могут быть включены в макрос, но макросы списков могут быть объединены из меньших списков. Вывод меньшего списка может быть условным.
Решение находится в начале кода ниже, но я добавил вывод макроса списка для полноты. Также обратите внимание, что меньшие списки представляют собой списки одного элемента, но они могут также содержать несколько элементов.
//Inner macro parameter list.
//FUNC_(enumTag, function, description)
//Conditional sublist.
#ifdef COND1
#define COND1_FUNC_LIST \
FUNC_(FuncCOND_1, func1, “func1 description”)
#else
#define COND1_FUNC_LIST
#endif
//Conditional sublist.
#ifdef CONDN
#define CONDN_FUNC_LIST \
FUNC_(FuncCOND_N, funcn, “funcn description”)
#else
#define CONDN_FUNC_LIST
#endif
//Complete list.
#define FUNC_LIST \
COND1_FUNC_LIST \
CONDN_FUNC_LIST \
//Comment to terminate preprocessor continuation.
//The rest generates all of the code artifacts.
#define CONDN_FUNC_ENUM(enumTag, function, description) enumTag,
#define CONDN_FUNC_EXTERN(enumTag, function, description) extern int function(void);
#define CONDN_FUNC_INITIALIZER(enumTag, function, description) {function, description},
typedef int (*FuncPtr)(void);
typedef struct
{
FuncPtr function;
char * description;
} FuncStruct;
enum
{
FUNC_LIST(CONDN_FUNC_ENUM)
FuncCOUNT
};
FUNC_LIST(CONDN_FUNC_EXTERN)
FuncStruct funcs[FuncCOUNT] =
{
FUNC_LIST(CONDN_FUNC_INITIALIZER)
};