简体   繁体   中英

Using C X macros in combination with #ifdef

Assuming my code looks like in 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},
};

Is it possible to replace this with X macros in order to minimize the repetition of the function names and conditions in both parts?

Without the #ifdef CONDX conditions, this appears to be quite straight forward. However, I have no idea how to include them in the X macro, because it is not allowed to use #ifdef in a #define .

Not sure whether X macros are the solution here. You can, however, use a bit of preprocessor magic to reduce the typing. The problem remains the conditional compilation ( #ifdef s) in your example. Without knowing what these conditions look like it is difficult to reduce the amount of typing further.

Condider 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 is, I think, a step in the direction you are aiming for. To see what this 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).

Suppose you want to define COND1 but no 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 of the macros, but with different expansions.

You can't use #ifdef inside #define , so you can't use #define based X-macros, but you can still use #include based X-macros.

For example, use a file t.def :

#ifdef COND1
F(func1, "func1 description")
#endif
#ifdef COND2
F(func2, "func2 description")
#endif

And in your primary 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 processed to:

extern int func2(void);

mystruct funcs[] = {
    {"func2 description", func2},
    {NULL, NULL},
};

The key is, I think, to "factor out" the condition. For example, COND1 (or COND2 etc.) should surround the definition of the funcs array, not vice versa. Here I assume that you can boil down your conditions to integer defines. That could be done eg 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 parametrize your function name generation as follows. (The code makes use of string literal concatenation and of for loop variables. gcc -std=c99 compiles it fine.)

Main file. Declare the functions and define the description struct array.

#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;
}

File funcs.c 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 file xmacro_x.h provides the "X macros" expanded to different code snippets in the source files. Here it introduces various "function families", name stubs which will be combined with the numeric suffixes later.

// The "X macros" which will be expanded to different things later
X(func, N)
X(gunc, N)
//...

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 pieced together from smaller lists. The output of the smaller list can then be conditional.

The solution is at the start of the code below, but I've added the output of the list macro for completeness. Also, note that the smaller lists are lists of one item, but they could as 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)
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM