【问题标题】:How can I use a macro, in a macro, to transform a macro's arguments?如何在宏中使用宏来转换宏的参数?
【发布时间】:2017-05-14 17:01:14
【问题描述】:

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

由于我试图指定一次的数据涉及一些相同的宏翻译(忽略参数、前缀与后缀),我决定通过将它们重写为广义的 Meta X 来增强my original single-fit 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)

这在大多数用例中都有效:但是,在少数极端情况下我会遇到麻烦。首先,尝试连接参数会导致 transformation 宏的名称 被连接,而不是 transformed 名称:

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

我怎样才能让这些按照我想要的方式工作?

【问题讨论】:

    标签: c macros c-preprocessor


    【解决方案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_
    

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

    #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_ 之类的标识符不同,后者可以是#defined 并在调用点附近立即#undefined,此处使用的间接宏必须保持定义只要这些宏本身可以使用(显然),因此应该选择一个不会与代码库的其他部分发生冲突的名称(即,不仅仅是X_)。即使像APPLY_MACROas I used here 这样的东西也不是特别可取的:大型项目(尤其是任何可能重新分发以用于其他解决方案的代码)应该考虑在使用的名称前加上某种命名空间前缀,以限制其范围干扰其他上下文(如this SoftwareEngineering.se answer 中所述)。

    Ultimately,不过,出于我的目的,我最终解决了这个问题,方法是重写我的转换宏以应用宏并调用它转换后的参数,使解释变平并允许我完全重构上述APPLY_MACRO 宏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      相关资源
      最近更新 更多