为什么在libc中可以滥用宏

用户名

如果我告诉你我想实现以下代码:

static const uint8_t jump_table[] =
{
    /* ' ' */  1,            0,            0, /* '#' */  4,
               0, /* '%' */ 14,            0, /* '\''*/  6,
      ...
      ...
    /* 't' */ 27, /* 'u' */ 16,            0,            0,
    /* 'x' */ 18,            0, /* 'z' */ 13
};

#define LABEL(Name) do_##Name
#define NOT_IN_JUMP_RANGE(Ch) ((Ch) < ' ' || (Ch) > 'z')
#define CHAR_CLASS(Ch) (jump_table[(INT_T) (Ch) - ' '])
#define REF(Name) &&do_##Name
#define JUMP(ChExpr, table)                   \
do                                            \
{                                             \
    const void *ptr;                          \
    spec = (ChExpr);                          \
    ptr = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown) \
       : table[CHAR_CLASS (spec)];            \
    goto *ptr;                                \
} while (0)

#define TABLE                                 \
/* Step 0: at the beginning.  */              \
static JUMP_TABLE_TYPE step0_jumps[30] =      \
{                                             \
    REF (form_unknown),                       \
    REF (flag_space),       /* for ' ' */     \
    REF (flag_plus),        /* for '+' */     \
    REF (flag_minus),       /* for '-' */     \
     ...                                      \
    REF (flag_i18n),        /* for 'I' */     \
};

使用此用法示例:

void usage_example_function(void)
{
    do
    {
      TABLE;

      /* Get current character in format string.  */
      JUMP (*++f, step0_jumps);

      /* ' ' flag.  */
      LABEL (flag_space):
      space = 1;
      JUMP (*++f, step0_jumps);
      ...
      ...
    } while (something)
}

您会告诉我,在任何体面的编码风格标准下,这都是不可接受的,提交这样的代码最有可能弊大于利。

现在,glibc实施vfprintf的宏滥用程度远远超过此(上面的代码从那里获取),但是此代码是众多编译程序的一部分,并且经过多次测试,使我无法满足当今的任何(宏)编码标准缺乏理由。

为什么这样的宏滥用是错误的?或者,如果这种滥用是错误的,为什么我们接受这种libc实施?

埃里克·波斯蒂奇(Eric Postpischil)
  1. 样式规则是旨在改善软件工程师(包括团队中的当前工程师,将来将要使用代码的工程师以及将来的自己)之间的沟通的指南,旨在减少错误并提高软件开发效率。这些准则应与其他目标保持平衡,例如性能,必要性,解决在特定情况下出现的问题等等。

  2. 通常需要与编译器和语言实现相关的库中的软件用于特殊目的。它可能是C实现的一部分,可能需要与C编译器协调。因此,它并不完全受制于严格遵循C代码的约束,而是可以使用特定于C实现的构造或功能。(应该清楚地记录这种用法。)通常,用严格符合C的代码编写C实现是不可能的-C实现必须以C标准未指定的方式与硬件和操作系统软件进行交互。即使是在可能的情况下,出于性能和其他目标的考虑,也可能并不理想。

  3. 库的作用之一是完成所有“肮脏”的工作,因此您不必这样做,或者完成艰巨或复杂的工作。旨在供许多人使用的图书馆是投资大量工程工作的好地方,因为少数人的工作可以使许多人受益。考虑到glibc的直接和间接用户数量,投资带来的收益是巨大的,因此,如果能提高性能或可移植性,就值得制作一些奇怪的代码。在某种程度上,需要这种复杂的代码来获得期望的结果,或者比其他方法更需要这种代码,将这些代码放入一个库中可以通过将其放置在一个可供许多人使用的地方来减少世界上此类代码的数量。这样一来,他们便无需编写自己的相似代码即可达到相同的目的。

  4. 与某些宏相比,问题中显示的代码对预处理器宏的使用相当温和。除了GCC“将标签作为值”扩展名(&&用于获取标签的地址)之外,它还使用标准功能并构造了状态机,任何拥有软件工程学士学位的人都应该熟悉这种状态机。在研究了代码之后,经验丰富的软件工程师可以认识到它在做什么,并且可以轻松地使用代码。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么变量可以在Julia宏名称修饰中以#开头?

为什么glibc中的宏-ENOSYS是什么意思?

什么时候可以省略宏中参数周围的括号?

为什么Julia中的字符串宏使用...?

在Julia中,为什么@printf是宏而不是函数?

为什么不建议在头文件中定义宏?

为什么序列迭代在C宏中起作用?

为什么要对宏定义中的单个值使用括号?

为什么在Laravel中工作宏不响应?

Latex - 为什么注释环境在宏中不起作用?

为什么要用C中的其他宏递归定义宏?

“ __cplusplus”宏扩展为什么?

宏被视为什么类型?

为什么需要offsetof宏?

为什么将宏(子)分配给按钮不能从单独的模块中工作,尽管如果我把它放在(ThisOutlookSession)上它可以工作?

为什么不能在C ++中将putc()实现为宏?(或者可以吗?)

为什么宏定义“ #define yyparse ol_parser_parse”可以工作?

为什么可以关闭JDBI中的查询?

为什么Go中的错误是可以消除的?

为什么Java中的属性可以公开?

为什么可以从StackOverflowError中恢复?

为什么可以从函数中返回“向量”?

在 React 中,为什么可以保留输入

为什么即使ASLR启用,__ libc_start_main的地址在GDB中也总是相同?

为什么在libc ++(clang)中未实现std :: ios_base :: sync_with_stdio?

为什么libc中的内存分配没有路由到我的分配包装器?

您遇到过的最糟糕的现实世界宏/预处理器滥用情况是什么?

为什么这个在 Common Lisp 中漂亮地打印宏扩展的宏不起作用?有哪些替代工具?

为什么在math.h中而不是在float.h中定义了fpclassify()宏?