The preprocessor crashes due to - '#' is not accompanied by a macro parameter

I am trying to simplify an array declaration, but have run into a problem with the preprocessors that I use. My initial code is as follows:

#define REQ_ENTRY(parm_1, parm_2) \ #if defined(parm_1) \ { parm_1, parm_2 }, \ #endif typedef struct { int parm1, parm2; } MyTypedef_t; static const MyTypedef_t MyList[] = { REQ_ENTRY( ID_1, 1 ) REQ_ENTRY( ID_2, 2 ) REQ_ENTRY( ID_3, 3 ) REQ_ENTRY( ID_4, 4 ) REQ_ENTRY( ID_5, 5 ) }; 

The line does not work, of course, with the error message "error:" # "do not follow the macro parameter". The reason for this is explained here ( Why the compiler complains about this macro declaration )

Basically, I try to avoid declaring an array as follows:

 static const MyTypedef_t MyList[] = { #if defined (ID_1) { ID_1, 1 }, #endif #if defined (ID_2) { ID_2, 2 }, #endif #if defined (ID_3) { ID_3, 3 }, #endif #if defined (ID_4) { ID_4, 4 }, #endif #if defined (ID_5) { ID_5, 5 }, #endif }; 

The list can be quite long and vary depending on the type of project assembly. I tried to think about using x-macros, but I think I will have the same problem. I hope someone can see a way to create preprocessor macros so that I can get the original sugar syntax? Any insight is greatly appreciated.

+5
source share
2 answers

There is no clean, clean solution. But there are solutions to various ugliness.

If you do not mind, including both the identifier and the sequence in the macro definition, you can solve it as follows:

 #define CONCAT2(x,y) x##y #define CONCAT(x,y) CONCAT2(x,y) #define REQ_ENTRY_YES(p1, p2) { p1 , p2 } #define REQ_ENTRY_NO(p1) #define IS_PAIR_HELPER(a, b, c, ...) c #define IS_PAIR(...) IS_PAIR_HELPER(__VA_ARGS__, YES, NO) #define REQ_ENTRY(pair) CONCAT(REQ_ENTRY_, IS_PAIR(pair))(pair) #define ID_1 78723649, 1 #define ID_3 2347602, 3 typedef struct { int parm1, parm2; } MyTypedef_t; static const MyTypedef_t MyList[] = { REQ_ENTRY( ID_1 ) REQ_ENTRY( ID_2 ) REQ_ENTRY( ID_3 ) REQ_ENTRY( ID_4 ) REQ_ENTRY( ID_5 ) }; 

Run gcc with -std=c11 -Wall -E and show only the definition of MyList :

 static const MyTypedef_t MyList[] = { { 78723649 , 1 } { 2347602 , 3 } }; 

You can do the same using any second value in the #define ID_x macros, if any; real parameters can be added to REQ_ENTRY . But this requires some additional juggling.

+3
source

It is a pity that the defined operator is available only in the context of #if and #ifelse , but not for macro extensions. Be that as it may, I agree with the rice about the decisions of various ugliness.

This requires a solution that requires certain values ​​to be surrounded by parentheses. Then you can use the identifier as a regular value, and you can also pass it to DEF , which will expand to 1 if the macro is in parentheses, or 0 if not. (This is the trick I learned here .)

Using the DEF macro, you can create helper macros that extend or ignore this definition:

 /* Auxiliary macros */ #define M_CHECK(...) M_CHECK_(__VA_ARGS__) #define M_CHECK_(a, b, ...) b #define M_IS_PAREN(x) M_CHECK(M_IS_PAREN_ x, 0) #define M_IS_PAREN_(...) 1, 1 #define M_CONCAT(a, b) M_CONCAT_(a, b) #define M_CONCAT_(a, b) a ## b /* Conditional definition macros */ #define DEF(x) M_IS_PAREN(x) #define DEF_IF_0(id, def) #define DEF_IF_1(id, def) {id, def}, #define COND_DEF(x, y) M_CONCAT(DEF_IF_, DEF(x))(x, y) /* Implementation */ #define ID_1 (27) #define ID_3 (28) #define ID_4 (29) static const MyTypedef_t MyList[] = { COND_DEF(ID_1, 1) COND_DEF(ID_2, 2) COND_DEF(ID_3, 3) COND_DEF(ID_4, 4) COND_DEF(ID_5, 5) }; 

This will give:

 static const MyTypedef_t MyList[] = { {(27), 1}, {(28), 3}, {(29), 4}, }; 

You can also use the DEF macro in code that will be expanded to 0 or 1:

 printf("ID_1 is %s.\n", DEF(ID_1) ? "defined" : "undefined"); 
+1
source

Source: https://habr.com/ru/post/1232241/


All Articles