有趣的:扩展 C 预处理器
Fun Extending the C Preprocessor

原始链接: https://www.davidpriver.com/Fun-Extending-the-C-Preprocessor.html

这个 C 代码片段定义了一个预处理器 MAP 工具:`__map` 统计其可变参数并分派到 `MAP_n`,后者递归地将宏应用于每个参数。`PRINTINT` 将每个参数展开为 `printf` 调用,打印其文本形式和整数值。在 `main` 中,`__map(PRINTINT, 1,2,3,4,x,y,z)` 生成七条 `printf` 调用,显示如 `1 = 1`、`x = 1` 和 `z = 3` 的值。

Hacker News 讨论了 David Priver 关于扩展 C 预处理器的文章。评论者分享宏技巧,例如将 `__VA_ARGS__` 转换为 argc/argv,以及用于更简单 map 式展开的 WG14 `__VA_TAIL__` 提案。主要争论在于非平凡预处理是否值得:批评者指出编译慢、代码脆弱且不透明,并推荐 C++ 模板、Zig 或最小化宏;支持者认为宏可以保留类型安全,有时比 C++ 复杂性更简单。其他人指出编译器错误信息会隐藏展开后的宏文本,并解释为什么展开后的文本不会被存储。讨论还涉及 Lisp 式可编程性、编译器调试、预处理器图灵完备性以及现有 map 宏。有人建议 `__VA_COUNT__`、`__CAT__` 或 `#defrec` 等内建功能会有所帮助。总体而言,讨论在实用宏工具与避免过度使用预处理器技巧之间取得平衡。
相关文章

原文
// map.c
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a##b
#define NARG(...) \
  NARG_(__VA_ARGS__ __VA_OPT__(,) 8,7,6,5,4,3,2,1,0)
#define NARG_(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define MAP_0(m)
#define MAP_1(m, x)      m(x)
#define MAP_2(m, x, ...) m(x) MAP_1(m, __VA_ARGS__)
#define MAP_3(m, x, ...) m(x) MAP_2(m, __VA_ARGS__)
#define MAP_4(m, x, ...) m(x) MAP_3(m, __VA_ARGS__)
#define MAP_5(m, x, ...) m(x) MAP_4(m, __VA_ARGS__)
#define MAP_6(m, x, ...) m(x) MAP_5(m, __VA_ARGS__)
#define MAP_7(m, x, ...) m(x) MAP_6(m, __VA_ARGS__)
#define MAP_8(m, x, ...) m(x) MAP_7(m, __VA_ARGS__)
#define __map(m, ...) \
  CAT(MAP_, NARG(__VA_ARGS__))(m __VA_OPT__(,) __VA_ARGS__)
int x = 1;
int y = 2;
int z = 3;

#define PRINTINT(x) printf(#x " = %d\n", x);
int printf(const char*, ...);
int main(){
    __map(PRINTINT, 1, 2, 3, 4, x, y, z)
}
// map.i
int x = 1;
int y = 2;
int z = 3;
int printf(const char*, ...);
int main(){
    printf("1" " = %d\n", 1); // 1 = 1
    printf("2" " = %d\n", 2); // 2 = 2
    printf("3" " = %d\n", 3); // 3 = 3
    printf("4" " = %d\n", 4); // 4 = 4
    printf("x" " = %d\n", x); // x = 1
    printf("y" " = %d\n", y); // y = 2
    printf("z" " = %d\n", z); // z = 3
}
联系我们 contact @ memedata.com