如何在宏中使用宏来转换宏的参数?

时间:2016-12-30 11:22:08

标签: c macros c-preprocessor

我在我的项目中使用X Macros,以便在名称列表都需要对其执行相同操作的地方not repeat myself(例如创建,初始化,填充)和破坏)。

由于我试图指定的数据一次涉及一些相同的宏能翻译(忽略参数,前缀与后缀),我决定通过将它们重写为通用来增强my original single-fit X Macros < em> Meta X-Macros ,使用解释元宏参数的转换宏,可以定义多个衍生的最终用例拟合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) 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'

其次,转换两个参数的宏不会被扩展 - 它们作为单个令牌传递给X (对转换宏的调用) :

#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'

我如何按照我希望的方式进行这些工作?

1 个答案:

答案 0 :(得分:1)

other Stack Overflow questions中有关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_

第二个问题更阴险,但它有一个类似的解决方案:另一层间接。这里的关键技巧是,第一层必须采用单个参数传递给下一层,而不是复制最终宏的签名,然后将其扩展为多个参数: / p>

#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... */

但是,请注意,您选择用于APPLY_MACRO的任何令牌都不同于XX_之类的标识符,可以#define并立即{{1}在调用点附近,这里使用的间接宏必须保持定义,只要这些宏本身可以被使用(显然),所以应该选择一个名称不是&#39;可能导致与代码库的其他部分发生冲突(即,不仅仅是#undef)。甚至类似X_as I used here之类的东西也不是特别明智的:较大的项目(尤其是可以重新分发以用于其他解决方案的任何代码)应该考虑使用某种名称空间使用的名称前缀前缀以限制其范围干扰其他上下文(如this SoftwareEngineering.se answer中所述)。

Ultimately但是,出于我的目的,我最终通过重写我的转换宏来解决这个问题,以便将宏应用并使用转换的参数调用它,展平解释并允许我完全重构上述APPLY_MACRO宏。