How can I use a macro, in a macro, to transform a macro's arguments?

2 min read Original article ↗

I'm using X Macros in my project, so as to not repeat myself in places where a list of names all need to have identical operations performed on them (such as creation, initialization, population, and destruction).

As the data I'm trying to specify once involves a few of the same macro-able translations (ignoring arguments, prefixing vs. suffixing), I decided to enhance my original single-fit X Macros by rewriting them as generalized Meta X-Macros, from which multiple derivative end-use-case-fitting X Macros could be defined, using transformation macros that interpret the meta-macro's arguments:

// Meta-macros //

#define MAIN_WINDOW_TEXT_LAYERS_METAMACRO(macro, tr) \
  macro(tr(hour_layer)) \
  macro(tr(min_layer)) \
  macro(tr(date_layer))

#define MAIN_WINDOW_LAYERS_METAMACRO(macro, tr) \
  macro(tr(colon_layer)) \
  macro(tr(phone_batt_layer)) \
  macro(tr(watch_batt_layer))

#define GBITMAPS_WITH_RESOURCE_IDS_METAMACRO(macro, tr) \
  macro(tr(watch_icon, ICON_WATCH_6X11)) \
  macro(tr(watch_charging_icon, ICON_WATCH_CHARGING_6X11)) \
  macro(tr(phone_icon, ICON_PHONE_6X11)) \
  macro(tr(phone_charging_icon, ICON_PHONE_CHARGING_6X11))

#define GFONTS_WITH_RESOURCE_IDS_METAMACRO(macro, tr) \
  macro(tr(time_font, FONT_ARVO_BOLD_48)) \
  macro(tr(date_font, FONT_ARVO_BOLD_20))

// Transformation macros //

#define IDENTITY_MACRO(x) x
#define STATIC_PREFIX_MACRO(x) s_ ## x
#define STATIC_PREFIX_DISCARD_MACRO(x, _) s_ ## x
#define STATIC_PREFIX_RESOURCE_ID_PREFIX_MACRO(x, id) \
  s_ ## x, RESOURCE_ID_ ## s

// Derived X-Macros //

#define FOR_MAIN_WINDOW_STATIC_TEXT_LAYER_POINTERS(macro) \
  MAIN_WINDOW_TEXT_LAYERS_METAMACRO(macro, STATIC_PREFIX_MACRO)

#define FOR_MAIN_WINDOW_STATIC_LAYER_POINTERS(macro) \
  MAIN_WINDOW_LAYERS_METAMACRO(macro, STATIC_PREFIX_MACRO)

#define FOR_MAIN_WINDOW_LAYER_NAMES(macro) \
  MAIN_WINDOW_LAYERS_METAMACRO(macro, IDENTITY_MACRO)

#define FOR_STATIC_GFONTS(macro) \
  GFONTS_WITH_RESOURCE_IDS_METAMACRO(macro, STATIC_PREFIX_DISCARD_MACRO)

#define FOR_STATIC_GFONTS_WITH_RESOURCE_IDS(macro) \
  GFONTS_WITH_RESOURCE_IDS_METAMACRO(macro, STATIC_PREFIX_RESOURCE_ID_PREFIX_MACRO)

#define FOR_STATIC_GBITMAP_POINTERS_WITH_RESOURCE_IDS(macro) \
  GBITMAPS_WITH_RESOURCE_IDS_METAMACRO(macro, STATIC_PREFIX_RESOURCE_ID_PREFIX_MACRO)

#define FOR_STATIC_GBITMAP_POINTERS(macro) \
  GBITMAPS_WITH_RESOURCE_IDS_METAMACRO(macro, STATIC_PREFIX_DISCARD_MACRO)

This works, in most use cases: however, there are a few edge cases where I run into trouble. Firstly, trying to concatenate arguments results in the transformation macro's name getting concatenated, rather than the transformed name:

#define X(name) layer_set_update_proc(s_ ## name, name ## _update_proc);
FOR_MAIN_WINDOW_LAYER_NAMES(X)
#undef X
error: implicit declaration of function 's_IDENTITY_MACRO'

Secondly, macros that transform two arguments aren't getting expanded - they're being passed to X as a single token (the call to the transformation macro):

#define X(name, id) name = fonts_load_custom_font(resource_get_handle(id));
FOR_STATIC_GFONTS_WITH_RESOURCE_IDS(X)
#undef X
error: macro "X" requires 2 arguments, but only 1 given
error: unknown type name 'X'
#define X(name, id) name = gbitmap_create_with_resource(id);
FOR_STATIC_GBITMAP_POINTERS_WITH_RESOURCE_IDS(X)
#undef X
error: macro "X" requires 2 arguments, but only 1 given
error: expected '=', ',', ';', 'asm' or '__attribute__' before 'X'

How can I make these work the way I want them to?