【问题标题】:C preprocessor macro that emits a struct if some other macro is defined?如果定义了其他宏,则发出结构的 C 预处理器宏?
【发布时间】:2021-07-19 15:30:51
【问题描述】:

我正在尝试定义一个 Cpp 宏,例如 ENO_DECL(eno, func) 扩展如下

#define ENO_DECL(eno, func) { eno, #eno, func }

所以我可以写,例如,ENO_DECL(ENOMEM, resource_error) 当然可以。有些平台可能不会定义所有错误,所以我们需要这样的东西:

#ifdef EXYZ
ENO_DECL(EXYZ, type_error)
#endif

这当然不是那么优雅。如果未定义 EXYZ,我想摆脱 #ifdef 并将 ENO_DECL() 扩展为空。我已经阅读了一些有趣的帖子和博客,其中包含非常好的 cpp 技巧,但似乎没有涵盖这一点。我觉得这可以做到(便携式)。那正确吗? ...以及如何?

【问题讨论】:

  • 我记得一个非常好的预处理器时间教程if 可以放入宏中,请参阅jhnet.co.uk/articles/cpp_magic 也许你可以使用它?请注意,它不能很好地替代ifdef,因为如果它找到一个未定义的宏,它将返回true,可能只是一个非常有限的if,它会查看宏的计算结果是否为true或错误的。所以这可能不是最好的解决方案。
  • 类似于this?
  • @HWalters 我认为你是对的。不幸的是,除非您需要多次宏(我不需要),否则它并没有比在任何地方直接使用#ifdef 更好。 @mediocrevegetable1 ifdef 是我需要的 :(

标签: c macros c-preprocessor


【解决方案1】:

理想情况下,您应该从一开始就摆脱所有这些宏。这些不是“好技巧”,它们是最后的手段,当你维护一些写得很糟糕的代码并且不得不用奇怪的函数式宏和其他绝望的措施来修补所有东西时。

无论如何,您正在寻找的解决方案似乎是“X 宏”。维基百科等解释了它们的工作原理。基本上,这是一种“行业事实上的”标准化方式,将所有代码重复集中到代码库中的单个位置。对代码维护很有用,但在开发新代码时应避免使用。一个例子:

#define ENO_LIST(X)         \
  X(ENOMEM, resource_error) \
  X(ENOFOO, foo_error)      \
  ...

typedef struct
{
  int eno;
  const char* str;
  void (*handler)(void);
} eno_t;

const eno_t eno [ENO_N] = 
{
  #define X(n, func) {n, #n, func},
    ENO_LIST(X)
  #undef X
};

这个结构列表将扩展为:

const eno_t eno [ENO_N] = 
{
  {ENOMEM, "ENOMEM", resource_error},
  {ENOFOO, "ENOFOO", resource_error},
  ...
};

如果ENOMEM 是索引为0 到N 的enum 的一部分,您可以像这样进一步提高数据完整性:

typedef enum
{
  ENOMEM,
  ENOFOO,
  ...
  ENO_N
} enom;

...

#define X(n, func) [n] = {n, #n, func},  // [n] is a designated initializer

...

_Static_assert(sizeof eno / sizeof *eno == ENO_N, "Enum and struct don't match");

【讨论】:

    【解决方案2】:

    我想摆脱#ifdef

    这是不可能的。您可以使用一些代码生成工具或其他预处理工具。例如,使用m4:

    #include <errno.h>
    
    /** m4 code here
    define(`ENO', `#ifdef $1
        ENO_DECL($1, $2),
    #endif')
    */
    #define ENO(x) /* just so that IDE does not complain */
    #define ENO_DECL(eno, func) { eno, #eno, func }
    
    typedef struct {
      int eno;
      const char* str;
      void (*handler)(void);
    } eno_t;
    const eno_t eno [] = {
        ENO(ENOMEM, abc)
        ENO(E_DOES_NOT_EXISTS, def)
    };
    

    您可以使用例如m4 file.c.m4 | gcc -E -xc - 检查此类代码,预处理器输出为:

    const eno_t eno [] = {
    
     { 
    # 16 "<stdin>" 3 4
    12
    # 16 "<stdin>"
    , "ENOMEM", abc },
    
    
    
    
    };
    

    【讨论】:

      猜你喜欢
      • 2011-01-26
      • 2012-05-27
      • 1970-01-01
      • 2010-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多