Using CX macros in combination with C # ifdef
Assuming my code looks like the following snippet:
#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},
};
Can we replace this with X macros to minimize duplication of function and condition names in both parts?
#ifdef CONDX
It looks pretty straight forward without conditions . However, I have no idea how to include them in the X macro because it is not allowed to be used #ifdef
in #define
.
source to share
Not sure if there are X macros here. However, you can use a little preprocessor magic to reduce typing. The problem remains conditional compilation ( #ifdef
s) in your example. Without knowing what these conditions look like, it is difficult to reduce the amount of typing.
Indicate the following:
#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
};
This, I think, is a step in the direction you are striving for. To see what it does you can try
gcc -E -DCOND1 <file-with-contents-above>.c
(if you are on some Unix) or
cl -E -DCOND1 <file-with-contents-above>.c
(if you are on Windows using Visual Studio).
source to share
Let's say you want to define COND1 but not CONDN. I think you can do the following:
#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
The problem is that you need to define all macros, but with different extensions.
source to share
You cannot use #ifdef
internally #define
, so you cannot use X based macros #define
, but you can still use X based macros #include
.
For example, use the file t.def
:
#ifdef COND1
F(func1, "func1 description")
#endif
#ifdef COND2
F(func2, "func2 description")
#endif
And in your main file:
#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},
};
This will be handled for:
extern int func2(void);
mystruct funcs[] = {
{"func2 description", func2},
{NULL, NULL},
};
source to share
The key, I think, is to "deflate" the condition. For example, COND1
(or COND2, etc.) should surround the array definition funcs
, not vice versa. Here I am assuming that you can boil down your terms to the whole. This can be done, for example, by
#if COND1
# define N 1
#elif COND2
# define N 2
// ...
#endif
Let's also assume that you have several function stubs (not only func
), which are all expanded to stub<n>
-like names. Then you can fully parameterize the function name generation as follows. (The code uses concatenation of string literals and variables for a loop. gcc -std=c99
Will compile it.)
Main file. Declare functions and define a struct array 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;
}
The funcs.c file actually defines the functions.
// 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"
Finally, the rather uninteresting xmacro_x.h file provides "X" macros that are extended for different pieces of code in the source files. Here he introduces various "function families", stub names that will later be combined with numeric suffixes.
// The "X macros" which will be expanded to different things later
X(func, N)
X(gunc, N)
//...
source to share
The key to using conditional compilation with x macros (I call them list macros) is to understand that preprocessor directives cannot be included inside a macro, but that list macros can be collected together from smaller lists. The result of a smaller list can be conditional.
The solution is at the beginning of the code below, but I've added a list macro output for completeness. Also, note that smaller lists are lists of a single item, but they can easily contain multiple items.
//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)
};
source to share