【问题标题】:Struct initialisation through macro overuse通过宏过度使用进行结构初始化
【发布时间】:2010-07-15 20:14:08
【问题描述】:

我有一些结构要初始化,手动操作会很乏味。我想创建一个宏来帮助我...但我不确定 C 预处理器是否足够好。

我有代表菜单的结构。它们仅由函数指针组成:

typedef uint8_t (*button_handler) (uint8_t);
typedef void (*pedal_handler) (void);
typedef void (*display_handler) (void);
typedef void (*menu_switch_handler) (void);

#define ON_BUTTON(x) uint8_t menu_frame_##x##_button (uint8_t button)
#define ON_PEDAL(x) void menu_frame_##x##_pedal (void)
#define ON_DISPLAY(x) void menu_frame_##x##_display (void)
#define ON_SWITCH(x) void menu_frame_##x##_switch (void)

typedef struct menu_frame {
   button_handler on_button;
   pedal_handler on_pedal;
   display_handler on_display;
   menu_switch_handler on_switch;
} menu_frame;

这允许我将函数和单独的函数编写为(.c 文件):

ON_BUTTON(blah) { ... }

和菜单作为(.h 文件):

ON_BUTTON(blah);
ON_DISPLAY(blah);
menu_frame menu_frame_blah = {
   menu_frame_blah_button,
   NULL,
   menu_frame_blah_display,
   NULL
};

有什么方法可以将菜单定义折叠成一个定义吗?当然,我可以做一些扩展 MENU(blah, menu_frame_blah_button, NULL, menu_frame_blah_display, NULL) 的事情,但是有什么办法:

  • 使其更短(NULL 或某个名称)
  • 从结构体前面去掉ON_BUTTON(...);的需要

理想情况下,我希望MENU(blah, button, NULL, display, NULL) 同时定义处理程序和菜单结构本身。例如,我不知道如何防止将最后一个术语扩展为ON_SWITCH(NULL)

或者我应该以其他方式接近它?

【问题讨论】:

  • 您是否正在尝试编写 MFC 的 C 版本? ;-)
  • 可能 :) 实际上一些嵌入的东西有很多不同的菜单状态(写很多行 ON_BUTTON(some_other_state) 就够愚蠢了)。

标签: c struct macros c-preprocessor stringification


【解决方案1】:

我以前编写过 Python 脚本来为我生成这种代码。您可能想走这条路,只需将脚本用于您的构建过程。

【讨论】:

    【解决方案2】:

    您不能在 C 中进行条件宏扩展,因此您的宏将根据参数进行不同的扩展,例如:您不能在宏定义中使用 #if。

    我猜你能得到的最好的应该是MENU(blah, ITEM(blah,button), NULL, ITEM(blah,display), NULL),由于缺乏条件扩展,你仍然需要一个单独的原型集。

    就我个人而言,我会编写一个简单的脚本来生成那种样板 C 代码。一种可以理解您所需语法的方法。在 Python 或任何最适合您的...

    【讨论】:

    • 我知道我不能在宏中执行标准条件,但我真的指望一些有趣的语法,如果它以 NULL 为代价,则减少为 NOOP,如果它是其他东西,则减少为声明.. . 但是,是的——即使这样也可能是不可能的。我会尝试在外部生成。
    • -1 因为我们的陈述是错误的。在 Boost 预处理器库中,它们具有那些类型的条件宏,根据宏参数可以做不同的事情,例如 BOOST_PP_EXPR_IF。
    • 那么它会做 NULL 检查吗?因为boost.org/doc/libs/1_41_0/boost/preprocessor/logical/bool.hpp 的源代码表明它确实需要预处理器将条件评估为 0 到 256 之间的数字。不过,BOOST_PP_EXPR_IIF 是一件非常聪明的事情。
    【解决方案3】:

    您可以单独在预处理器中编写条件、有限循环、默认参数和所有此类内容。 Boost 库在其预处理器部分中实现了其中的一些。 Boost 主要用于 C++,但预处理器的东西基本上也应该在 C 中工作。

    通过这些技术,您可以编写复杂但易于使用的宏。使用 C99 而不是 C89(您已命名初始化程序和 VA_ARGS)时,实现起来会更简单一些,但仍然如此。

    【讨论】:

    • 我投了反对票,因为您在没有任何示例或参考的情况下提出了声明。我不知道 boost 是否能满足我对预处理的需求,但它是一段太大的代码,如果没有任何特定的指针就无法查看。如果您可以展示一个可以解决此问题的示例,那将很有用-否则就像在说“是的,这是可能的,但是您必须自己在那一大堆神秘的库代码中找到它”-不是有帮助。
    • @viraptor:我没有给出例子,因为它确实很复杂(正如我所说的)并且不适合这里的适当答案。 (好吧,我没有提到 boost,但我想你会找到的。)其次,我发现 boost 的预处理器部分并不那么神秘。它具有您在接受的答案中所要求的条件。然后只是在没有给出理由或要求更多信息的情况下投票……也没有多大帮助。只是粗鲁,我会说是由寻求帮助的人说的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 2021-09-03
    相关资源
    最近更新 更多