繁体   English   中英

什么会使 C++ 预处理器宏成为公认的开发工具?

[英]What would make C++ preprocessor macros an accepted development tool?

显然C++ 中的预处理器宏是

C++ 社区有理由害怕和回避。

但是,在某些情况下C++ 宏是有益的

将预处理器宏视为非常有用,并且可以以非常直接的方式减少重复代码——

- 给我留下一个问题,究竟是什么让预处理器宏“邪恶”,或者,正如问题标题所说,预处理器宏需要哪些功能(或删除功能)才能使它们作为“好”有用" 开发工具(而不是一个大家在使用时都害臊的填空题)。 (毕竟,Lisp 语言似乎包含宏。)

请注意:这与#include#pragma#ifdef无关 这是关于#define MY_MACRO(...)...

注意:我不打算让这个问题变得主观。 如果您认为是,请随时投票将其移至programmers.SE。

宏被广泛认为是邪恶的,因为预处理器是一个愚蠢的文本替换工具,几乎没有 C/C++ 知识。

C++ FAQ Lite中可以找到为什么宏是邪恶的四个很好的理由。

在可能的情况下,模板和内联函数是更好的选择。 我能想到为什么 C++ 仍然需要预处理器的唯一原因是用于#include和注释删除。

一个广受争议的优势是使用它来减少代码重复; 但是正如您从 boost 预处理器库中看到的那样,必须付出很多努力来滥用预处理器来处理诸如循环之类的简单逻辑,从而导致语法丑陋。 在我看来,用真正的高级编程语言编写脚本来生成代码而不是使用预处理器是一个更好的主意。

大多数预处理器滥用来自误解,引用 Paul Mensonides( Boost.Preprocessor库的作者)的话:

几乎所有与滥用预处理器有关的问题都源于试图使类对象宏看起来像常量变量,而类函数宏调用看起来像底层语言 function 调用。 充其量,类似函数的宏调用和 function 调用之间的相关性应该是偶然的。 永远不应将其视为目标。 这是一种从根本上破碎的心态。

由于预处理器很好地集成到 C++ 中,因此更容易模糊界限,大多数人看不出有什么区别。 比如让别人写一个宏把两个数相加,大多数人会这样写:

#define ADD(x, y) ((x) + (y))

这是完全错误的。 通过预处理器运行它:

#define ADD(x, y) ((x) + (y))
ADD(1, 2) // outputs ((1) + (2))

但答案应该是 3,因为将 1 加到 2 是 3。然而,编写了一个宏来生成 C++ 表达式。 不仅如此,它可以被认为是 C++ function,但它不是。 这就是导致滥用的地方。 它只是生成一个 C++ 表达式和一个 function 是 go 的更好方法。

此外,宏根本不像函数那样工作。 预处理器通过扫描和扩展宏的过程来工作,这与使用调用堆栈调用函数有很大不同。

有时宏生成 C++ 代码是可以接受的,只要它不模糊线条即可。 就像您使用 python 作为预处理器来生成代码一样,预处理器也可以这样做,并且具有不需要额外构建步骤的优点。

此外,预处理器可以与 DSL 一起使用,例如herehere ,但这些 DSL 在预处理器中具有预定义的语法,用于生成 C++ 代码。 它并没有真正模糊线条,因为它使用了不同的语法。

宏有一个显着的特点——它们很容易被滥用,而且很难调试。 您可以使用宏编写几乎任何东西,然后将宏扩展为单行代码,当没有任何效果时,您很难调试生成的代码。

仅此功能就让人思考十次是否以及如何使用宏来完成他们的任务。

并且不要忘记宏在实际编译之前会被扩展,因此它们会自动忽略名称空间、范围、类型安全和大量其他内容。

宏最重要的是没有scope,不关心上下文。 它们几乎是一个转储文本替换工具。 因此,当您 #define max(.... 时,任何有最大值的地方都会被替换;因此,如果有人在其标题中添加了过于通用的宏名称,他们往往会影响他们不打算影响的代码。

另一件事是,如果不小心使用它们,它们会导致代码非常难以阅读,因为没有人可以很容易地看到宏可以计算出的结果,尤其是在嵌套多个宏时。

一个好的指导方针是选择唯一的名称,并且在生成样板代码时,尽快#undef 它们以免污染命名空间。

此外,它们不提供类型安全或重载。

有时宏可以说是生成样板代码的好工具,例如在 boost.pp 的帮助下,您可以创建一个宏来帮助您创建枚举,例如:

ENUM(xenum,(a,b,(c,7)));

可以扩展到

enum xenum { a, b, c=7 };

std::string to_string( xenum x ) { .... }

像 assert() 这样需要对 NDEBUG 做出反应的东西通常也更容易实现为宏

C 开发人员使用宏和 C++ 开发人员使用模板有很多用途。

显然存在一些有用的极端情况,但大多数时候,C 世界的坏习惯适用于 C++ 的人们相信有这样一种称为 C/C++ 的语言

所以说“它是邪恶的”比冒着开发者滥用它们的风险更容易。

强迫程序员对宏使用正确的命名......以及更好的工具来跟踪宏的替换将解决我的大部分问题。 到目前为止,我真的不能说我遇到了重大问题......这是你自己烧伤自己并学会以后特别小心的事情。 但他们迫切需要更好地与 IDE、调试器集成。

  1. 宏不提供类型安全
  2. 参数执行两次的问题,例如 #define MAX(a,b) ((a)>(b)? (a): (b)) 并将其应用于 MAX(i++, y--)
  3. 调试问题,因为它们的名称不会出现在符号表中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM