Use specific entry of an X macro

431 views Asked by At

I am using X macros to generate functions setting GPIOs to 0 or 1 (I generate around 60 functions to set around 30 GPIOs). Here is an example (I have just written this example, so the syntax may be wrong):

/* X(pin_name, pin_nb) */
#define CPLD_GPIOs \
  X(Pin0, 0) \
  X(Pin1, 1) \
  X(Pin2, 2) \
  X(Pin3, 3)

I generate the functions to access to these GPIOs:

#define X(pin_name, pin_nb) \
  static void SetOn_GPIO##pin_name (void) { \
    SetOn_GPIOpins(pin_nb);\
    }
    CPLD_GPIOs
#undef X

The same process exists for SetOff_GPIOXXX functions.

Is there a way I can access the function generated above by the compiler as SetOn_GPIOPin2 in an other part of the program without directly writing the function name? (In order to keep the code as global as possible)

At the end of preprocessing, we should only have SetOn_GPIOPin2(); (and not every X-macro entries) generated from X-Macro.

Before pre-processing:

void foo ()
{
  /* some code */

  /* 
   * Macro to generate the desired function.
   * For e.g: SetOn_GPIOPin2();
   */

  /* some code */
}

After pre-processing:

void foo ()
{
  /* some code */

  /* Function resulting of the pre-processing */
  SetOn_GPIOPin2();

  /* some code */
}
2

There are 2 answers

0
John Bollinger On BEST ANSWER

From comments on the question, your objective appears to be to protect against the case in which your X macro is modified to produce differently-named functions. If that's so, then I think you're making unnecessary work for yourself: whether those names are changed is under your (and any other project developers') control, and a name change such as you are concerned about will not go unnoticed very long. So don't change them.

But if you're determined to go this route then no, there is no way to make the preprocessor extract the function names generated by your macros from their larger replacement text. Instead, you would need to inject them, via the same macro, into both the X macros and your other code. Like this, perhaps:

/* X(pin_name, pin_nb) */
#define CPLD_GPIOs(gen) \
  X(Pin0, 0, gen) \
  X(Pin1, 1, gen) \
  X(Pin2, 2, gen) \
  X(Pin3, 3, gen)

// Generates the wanted function names:
#define GPIO_ON(pin_name, pin_nb) SetOn_GPIO##pin_name

#define X(pin_name, pin_nb, gen) \
  static void gen(pin_name, pin_nb) (void) { \
    SetOn_GPIOpins(pin_nb);\
    }
    CPLD_GPIOs
#undef X

// ...

void some_function(void) {
    GPIO_ON(pin_name, pin_nb)();
}

But note well that although this technique might have other applications, such as to generating multiple sets of functions with the same set of X macros, it just kicks the can down the road with respect to the specific objective you described. You can rely on the name-generator macro to produce the same names for function declarations and function calls, but you still have the problem that the X macro can be modified to generate function declarations with different names.

5
Quentin On

Using our trusty Boost.Preprocessor rocket launcher, this is preatty easy:

#include <boost/preprocessor/tuple/rem.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/comparison/equal.hpp>

#define X_SELECT_PIN(pin_nb, selected_pin_nb, ...)  \
  BOOST_PP_TUPLE_REM_CTOR(BOOST_PP_IF(              \
    BOOST_PP_EQUAL(pin_nb, selected_pin_nb),        \
    (__VA_ARGS__),                                  \
    ()                                              \
  ))


// Usage

#define X(pin_name, pin_nb)\
  X_SELECT_PIN(pin_nb, 2, SetOn_GPIO##pin_name (void);)
  CPLD_GPIOs
#undef X

This uses BOOST_PP_IF to expand your pattern only for the selected pin. The added parentheses and BOOST_PP_TUPLE_REM_CTOR are there to protect the macro from expansions containing commas.