【问题标题】:#ifdef inside a #define?#ifdef 在#define 中?
【发布时间】:2021-02-17 21:51:24
【问题描述】:

我在这样的定义的帮助下初始化一个结构数组:

#define FLAGCODE(name) { #name, MNT_ ## name }
    struct {
        const char  *name;
        uint64_t     flag;
    } flagcodes[] = {
        FLAGCODE(ACLS),
        FLAGCODE(ASYNC),
...

这很好用,现在我想添加一个检查,是否在没有为每个符号插入 #ifdef#endif 的情况下定义了每个标志(例如 MNT_ACLS)手动?

也就是说,我希望宏 FLAGCODE(name) 扩展为(相当于):

#ifdef MNT_ ##name
    { # name, MNT_ ##name },
#endif

例如,如果nameNOATIME,则代码变为:

#ifdef MNT_NOATIME
   { "NOATIME", MNT_NOATIME },
#endif

是的,我意识到,这意味着 double 会通过预处理器,因此不太可能 - 如果没有自定义代码生成器...但仍然...

【问题讨论】:

  • 您能否发布一个示例,说明您希望做什么以防万一它起作用?
  • 看看X macros。这是定义可以扩展到多个上下文的列表的常见模式。
  • 我会考虑编写一个代码生成脚本,并使用它来将规范编译为必要的(详细)C 代码。您不能将#ifdef / ... / #endif 放在宏定义的主体中。
  • 您提出的模式不起作用,因为 - 正如您所说 - #if... 宏扩展后不会重新评估表单。我同意 Jonathan 的观点,即使用一种可能存在于您所针对的所有编译平台上的语言编写一个小代码生成器 - 从长远来看 - 不太可能比深度预处理器技巧更令人头疼......即使你需要使用 C,但如果你有的话,awk、perl 或 python 会更好。
  • 谢谢,@Gene。当然,代码生成器是可能的——但这个问题是关于“深度预处理器技巧”:-) 有吗? 不是通过 CPP 进行双通——可以打开相同的宏如果它的参数没有定义,则变成一个空白?

标签: c preprocessor


【解决方案1】:

有一个解决方案,但强烈不推荐!你可以用 C 预处理器做一些有趣的事情(参见Macro to replace nested for loops 和问题中的链接)。但我再说一遍:不要这样做。这是滥用 cpp。

简而言之,您必须使用宏创建自己的#ifdef。在下面的代码中,ISDEF 是一个“运算符”,用于检查标志是否已定义以及 #if 是否已重新定义:IIF (为理解,所有解释都在这里:https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define COMMA ,

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

#define ISDEF(x) CHECK(PRIMITIVE_CAT(ISDEF_, x))
#define ISDEF_ PROBE(~)

#define FLAGCODE(name) IIF(ISDEF(name))({ #name COMMA MNT_ ## name }COMMA)

#define ACLS
#define FLAGDEFINED

int main()
{
        struct {
        const char  *name;
        uint64_t     flag;
    } flagcodes[] = {
        FLAGCODE(ACLS)
        FLAGCODE(ASYNC)
        FLAGCODE(FLAGDEFINED)
        FLAGCODE(FLAGNOTDEFINED)
        ...

你也可以用你的标志做一个列表(参见http://jhnet.co.uk/articles/cpp_magic中的MAP部分)。

享受但不要过度使用预处理器。

在克里斯·多德(Chris Dodd)的非常好的评论之后,
1:如果标志被定义为空(#define FLAGDEFINED),则此技巧有效。例如,它不适用于 #define FLAGDEFINED 1 或 #define FLAGDEFINED xxx。
2:添加了CPP_前缀,名称由CPP_FLAG更改

#define CPP_PRIMITIVE_CAT(CPP_a, ...) CPP_a ## __VA_ARGS__
#define CPP_COMMA ,

#define CPP_IIF(CPP_c) CPP_PRIMITIVE_CAT(CPP_IIF_, CPP_c)
#define CPP_IIF_0(CPP_t, ...) __VA_ARGS__
#define CPP_IIF_1(CPP_t, ...) CPP_t

#define CPP_CHECK_N(CPP_x, CPP_n, ...) CPP_n
#define CPP_CHECK(...) CPP_CHECK_N(__VA_ARGS__, 0,)
#define CPP_PROBE(CPP_x) CPP_x, 1,

#define CPP_ISDEF(CPP_x) CPP_CHECK(CPP_PRIMITIVE_CAT(CPP_ISDEF_, CPP_x))
#define CPP_ISDEF_ CPP_PROBE(~)

#define CPP_FLAGCODE(CPP_FLAG) CPP_IIF(CPP_ISDEF(CPP_FLAG))({ #CPP_FLAG CPP_COMMA MNT_ ## CPP_FLAG }CPP_COMMA)

#define ACLS
#define FLAGDEFINED

【讨论】:

  • 请注意,这仅在 name 被定义为 empty 宏时有效。如果它被定义为非空的东西,它就好像它没有被定义一样。
猜你喜欢
  • 2011-07-31
  • 2010-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-04
  • 1970-01-01
  • 2014-01-26
  • 2015-01-10
相关资源
最近更新 更多