Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to eliminate a redundant macro parameter

A while ago, I wrote a set of X-macros for a largish project. I needed to maintain coherent lists of both strings and enumerated references/hash values/callback functions etc. Here is what the function callback looks like

#define LREF_LOOKUP_TABLE_TEXT_SIZE 32
#define _LREF_ENUM_LIST(_prefix,_ref,...) _prefix ## _ ## _ref,
#define _LREF_BASE_STRUCT_ENTRY(_prefix,_ref) .text= #_ref "\0", .position= _LREF_ENUM_LIST(_prefix, _ref)
#define _LREF_FUNCTION_STRUCT_LIST(_prefix,_ref,...) {_LREF_BASE_STRUCT_ENTRY(_prefix,_ref) _prefix ## _ ## _ref ## _callback},

#define _LREF_ENUM_TYPEDEF(_prefix)                                               \ 
    typedef enum _prefix                                                          \  
    {                                                                             \  
        _ ## _prefix ## s(_prefix,_LREF_ENUM_LIST)                                \ 
        _LREF_ENUM_LIST(_prefix,tblEnd)                                           \ 
    } e_ ## _prefix

#define _LREF_LOOKUP_TABLE_TYPEDEF(_prefix, _extras)                              \ 
    typedef struct _prefix ## _lookup                                             \ 
    {                                                                             \ 
        const char text[LREF_LOOKUP_TABLE_TEXT_SIZE];                             \ 
        e_ ## _prefix position;                                                   \ 
        _extras                                                                   \ 
    } _prefix ##_lookup_t

#define LREF_GENERIC_LOOKUP_TABLE(_prefix, _type, _tabledef, _listdef, _extras)   \ 
    _LREF_ENUM_TYPEDEF(_prefix);                                                  \ 
    _LREF_LOOKUP_TABLE_TYPEDEF(_prefix,_tabledef);                                \ 
    _extras                                                                       \ 
    _LREF_LOOKUP_TABLE_DECLARATION(_prefix,_listdef, _type)

#define LREF_FUNCTION_LOOKUP_TABLE(_prefix, _type)                                \ 
    _ ## _prefix ## s(_prefix, _LREF_FUNCTION_DEF )                               \ 
    LREF_GENERIC_LOOKUP_TABLE(    _prefix,                                        \ 
        _type,                                                                    \ 
        void* (*function) (void*);,                                               \ 
    _LREF_FUNCTION_STRUCT_LIST,  )

This sits in a header file and allows me to write things like:

#define _cl_tags(x,_)         \
    _(x, command_list)        \
    _(x, command)             \
    _(x, parameter)           \
    _(x, fixed_parameter)     \
    _(x, parameter_group)     \
    _(x, group)               \ 
    _(x, map)                 \
    _(x, transform)

LREF_FUNCTION_LOOKUP_TABLE(cl_tag, static);

This keeps processing routines short. For example, loading a configuration file with the above tags is simply:

for (node_tag = cl_tag_lookup_table; node_tag->position != cl_tag_tblEnd; node_tag++)
{
    if (strcasecmp(test_string, node_tag->text) == 0)
    {
        func_return = node_tag->function((void*)m_parser);
    }
}

My question is this: I hate that I have to include the second parameter in my #define. I want to be able to write #define _cl_tags(_) instead of #define _cl_tags(x,_). As you can see, the x is only used to pass the prefix (cl_tag) down. But this is superfluous as the prefix is a parameter to the initial macro.

The solution to this would be easy if my preprocessor would expand the outer-most macros first. Unfortunately, GCC's preprocessor works through the inner-most macros, i.e. parameter values, before expanding the outermost macro.

I am using gcc 4.4.5


Clarification By C89 (and C99) standard, the following definitions

#define plus(x,y) add(y,x)
#define add(x,y) ((x)+(y))

with the invocation

plus(plus(a,b),c)

should yield

  1. plus(plus(a,b),c)
  2. add(c,plus(a,b))
  3. ((c)+(plus(a,b))
  4. ((c)+(add(b,a))
  5. ((c)+(((b)+(a))))

gcc 4.4.5 gives

  1. plus(plus(a,b),c)
  2. plus(add(b,a),c)
  3. plus(((b)+(a)),c)
  4. add(c,((b)+(a)))
  5. ((c)+(((b)+(a))))
like image 207
Seth Avatar asked Feb 21 '11 13:02

Seth


People also ask

How to undefine macro c++?

To remove a macro definition using #undef, give only the macro identifier, not a parameter list. You can also apply the #undef directive to an identifier that has no previous definition. This ensures that the identifier is undefined. Macro replacement isn't performed within #undef statements.

How do you pass a macro as a parameter?

Passing parameters to a macro. A parameter can be either a simple string or a quoted string. It can be passed by using the standard method of putting variables into shared and profile pools (use VPUT in dialogs and VGET in initial macros).


1 Answers

Here's what I would do (have done similarly):

Put these in a utility header file:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

Then define this before including your LREF macro header file:

#define LREF_TAG cl_tag

Then, in your LREF macro header file,

#define LREF_PFX(x) PPCAT(LREF_TAG, x)
#define LREF_SFX(x) PPCAT(x, LREF_TAG)

Then replace every instance of _prefix ## foo with LREF_PFX(foo) and foo ## _prefix with LREF_SFX(foo).

(When pasting more than two tokens together, just use nested PPCAT's.)

Your invocation would become

#define LREF_TAG cl_tag
#define _cl_tags(_)        \
    _(command_list)        \
    _(command)             \
    _(parameter)           \
    _(fixed_parameter)     \
    _(parameter_group)     \
    _(group)               \ 
    _(map)                 \
    _(transform)

LREF_FUNCTION_LOOKUP_TABLE(static);
like image 151
Jim Balter Avatar answered Oct 16 '22 10:10

Jim Balter