Preprocessor fails due to - '#' is not followed by a macro parameter

16,744

Solution 1

There is no nice clean solution. But there are solutions of varying ugliness.

If you don't mind including both the id and the sequence in the macro definition, it can be solved like this:

#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 through gcc with -std=c11 -Wall -E, and showing only the MyList definition:

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

    { 2347602 , 3 }


};

You can do the same thing by using any second value in the #define ID_x macros, as long as there is one; the real parameters can be added to REQ_ENTRY. But it takes some extra juggling.

Solution 2

It's a pity that the defined operator is available only in the context of #if and #ifelse, but not for macro expansions. As it stands, I agree with rici about the solutions of varying ugliness.

Here's a solution that requires that the defined values be surrounded by parentheses. You can then use the ID as regular value and you can also pass it to DEF, which expands to either 1 when the macro is in parentheses or 0 if not. (That's a trick I learned here.)

With the help of the DEF macro, you can create auxiliary macros that expand or ignore the given 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 produce:

static const MyTypedef_t MyList[] = {
    {(27), 1},

    {(28), 3},
    {(29), 4},

};

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

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

Solution 3

This is the closest I was able to get without introducing redundancy:

#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_NEWLINE /*
*/

#define REQ_ENTRY(parm_1, parm_2)                         \
PREPROCESSOR_IF defined(parm_1)      PREPROCESSOR_NEWLINE \
    { parm_1, parm_2 },              PREPROCESSOR_NEWLINE \
PREPROCESSOR_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 )
};

You need to run the preprocessor only once using -E and -CC and then compile the result of the first preprocessor pass, however it does not work due to (for example)

#if defined (ID_1) /*
*/ { ID_1, 1 }, /*
*/ #endif

not being recognized as distinct lines for the preprocessor, since the comments are replaced by just one space.

I was able to come up with a solution using redundancy, but it is hardly better than what you proposed (writing out the whole statement each time):

#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_DEFINE #define
#define PREPROCESSOR_NEWLINE /*
*/

#define REQ_ENTRY_1(parm_1, parm_2) PREPROCESSOR_IF defined(parm_1)
#define REQ_ENTRY_2(parm_1, parm_2) { parm_1, parm_2 }, 
#define REQ_ENTRY_3(parm_1, parm_2) PREPROCESSOR_ENDIF

PREPROCESSOR_DEFINE ID_1 (27)
PREPROCESSOR_DEFINE ID_3 (28)
PREPROCESSOR_DEFINE ID_4 (29)

typedef struct {
    int parm1,
        parm2;
} MyTypedef_t;

static const MyTypedef_t MyList[] =
{
    REQ_ENTRY_1( ID_1, 1 )
    REQ_ENTRY_2( ID_1, 1 )
    REQ_ENTRY_3( ID_1, 1 )

    REQ_ENTRY_1( ID_2, 2 )
    REQ_ENTRY_2( ID_2, 2 )
    REQ_ENTRY_3( ID_2, 2 )

    REQ_ENTRY_1( ID_3, 3 )
    REQ_ENTRY_2( ID_3, 3 )
    REQ_ENTRY_3( ID_3, 3 )

    REQ_ENTRY_1( ID_4, 4 )
    REQ_ENTRY_2( ID_4, 4 )
    REQ_ENTRY_3( ID_4, 4 )

    REQ_ENTRY_1( ID_5, 5 )
    REQ_ENTRY_2( ID_5, 5 )
    REQ_ENTRY_3( ID_5, 5 )
};

This does compile as desired using the two-step compilation process described above.

Share:
16,744

Related videos on Youtube

lordhog
Author by

lordhog

Updated on September 15, 2022

Comments

  • lordhog
    lordhog over 1 year

    I am trying to simplify the declaration of an array, but ran into an issue with the preprocessors that I am using. My initial code looks like the following:

    #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 build fails, of course, with the error message of "error: '#' is not followed by a macro parameter". The reason for this is explained here (Why compiler complain about this macro declaration)

    Basically, I am trying to avoid declaring the array as the follows, which does work:

    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 rather long and vary depending upon the build type of the project. I have tried thinking of using x-macros, but I think I would have the same issue. I am hoping someone might see a way of creating the preprocessor macros in such a way that I could achieve the original sugar syntax? Any insight is greatly appreciated.

    • Jonathan Leffler
      Jonathan Leffler over 8 years
      You can wrap #ifdef around a macro definition; you can't embed it inside one. Also, you can't directly test whether an argument to a macro is a defined macro. Unfortunately, the nay-saying is the easy part; it is not clear that there's a good way to avoid your original code. That's what I've used in equivalent circumstances.