C 如何在宏中使用宏来转换宏';谁的论点?

C 如何在宏中使用宏来转换宏';谁的论点?,c,macros,c-preprocessor,C,Macros,C Preprocessor,我在我的项目中使用,以便在名称列表都需要对其执行相同操作(例如创建、初始化、填充和销毁)的地方使用 由于我曾经试图指定的数据涉及一些相同的可宏转换(忽略参数、前缀和后缀),因此我决定将它们重写为广义元X-宏,从中可以定义多个派生的最终用例拟合X-宏,以增强性能,使用解释元宏参数的转换宏: // Meta-macros // #define MAIN_WINDOW_TEXT_LAYERS_METAMACRO(macro, tr) \ macro(tr(hour_layer)) \ mac

我在我的项目中使用,以便在名称列表都需要对其执行相同操作(例如创建、初始化、填充和销毁)的地方使用

由于我曾经试图指定的数据涉及一些相同的可宏转换(忽略参数、前缀和后缀),因此我决定将它们重写为广义元X-宏,从中可以定义多个派生的最终用例拟合X-宏,以增强性能,使用解释元宏参数的转换宏:

// 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)
#define X_(name, id) name = fonts_load_custom_font(resource_get_handle(id));
#define X(args) X_(args)
FOR_STATIC_GFONTS_WITH_RESOURCE_IDS(X)
#undef X
#undef X_
这在大多数用例中都是有效的:然而,在一些边缘情况下,我遇到了麻烦。首先,尝试连接参数会导致连接转换宏的名称,而不是转换后的名称:

其次,转换两个参数的宏没有得到扩展-它们被作为单个标记传递给
X
(对转换宏的调用):

如何使它们按我希望的方式工作?

正如在关于C预处理器的文章中所提到的,可以通过向宏引入另一层间接求值来避免第一个问题(宏的标识符被连接而不是其内容):

#define X_(name) layer_set_update_proc(s_ ## name, name ## _update_proc);
#define X(name) X_(name)
FOR_MAIN_WINDOW_LAYER_NAMES(X)
#undef X
#undef X_
第二个问题更阴险,但它有一个类似的解决方案:另一个间接层。这里的关键技巧是,第一层必须采用一个单参数传递到下一层,然后扩展到多个参数,而不是复制最后一个宏的签名:

// 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)
#define X_(name, id) name = fonts_load_custom_font(resource_get_handle(id));
#define X(args) X_(args)
FOR_STATIC_GFONTS_WITH_RESOURCE_IDS(X)
#undef X
#undef X_
当然,将这类变通方法转移到使用点,而不是在定义它们的地方修复它们,这不是健壮的工程,因此应该将这一间接层定义为元宏本身的一部分:

但是,请注意,您选择用于
APPLY_MACRO
的任何标记,都不同于
X
X
这样的标识符,它们可以
\define
d并立即在调用点附近定义
\undef
,这里使用的间接宏必须保持定义状态,只要这些宏本身可以使用(显然),因此应该选择一个不容易与代码库的其他部分(即,不仅仅是
X
)发生冲突的名称。即使像Apple YyGrace之类的东西也不是特别可取的:更大的项目(尤其是可以在其他解决方案中被重新分配的任何代码)应该考虑用某种名称空间前缀预先命名名称,以限制其范围不干扰其他上下文(如所描述的)。 不过,出于我的目的,我最终解决了这个问题,我重新编写了转换宏,使宏应用并使用转换后的参数调用它,简化了解释,并允许我重构上面描述的
apply\u宏

#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'
#define X_(name) layer_set_update_proc(s_ ## name, name ## _update_proc);
#define X(name) X_(name)
FOR_MAIN_WINDOW_LAYER_NAMES(X)
#undef X
#undef X_
#define X_(name, id) name = fonts_load_custom_font(resource_get_handle(id));
#define X(args) X_(args)
FOR_STATIC_GFONTS_WITH_RESOURCE_IDS(X)
#undef X
#undef X_
#define APPLY_MACRO(x, t) x(t)

#define MAIN_WINDOW_TEXT_LAYERS_METAMACRO(X, tr) \
  APPLY_MACRO(X,tr(hour_layer)) \
  APPLY_MACRO(X,tr(min_layer)) \
  APPLY_MACRO(X,tr(date_layer))

/* etc... */