什么会让 C++预处理器宏是公认的开发工具吗?

发布于 2024-11-29 21:32:00 字数 894 浏览 0 评论 0原文

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

C++ 社区的恐惧和回避是有道理的。

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

认为预处理器宏可以非常有用和< a href="https://stackoverflow.com/questions/96196/when-are-c-macros-beneficial/96316#96316">可以减少重复以非常简单的方式编写代码 --

-- 给我留下了一个问题,到底是什么使预处理器宏变得“邪恶”,或者,正如问题标题所说,哪个功能(或需要从预处理器宏中删除功能),以使其成为“良好”的开发工具(而不是每个人在使用它时都感到羞耻的填充)。 (毕竟,Lisp 语言似乎包含宏。)

请注意:这不是关于#include#pragma< /code> 或 #ifdef。这是关于#define MY_MACRO(...) ...

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

Apparently the preprocessor macros in C++ are

justifiably feared and shunned by the C++ community.

However, there are several cases where C++ macros are beneficial.

Seeing as preprocessor macros can be extremely useful and can reduce repetitive code in a very straightforward manner --

-- leaves me with the question, what exactly is it that makes preprocessor macros "evil", or, as the question title says, which feature (or removal of feature) would be needed from preprocessor macros to make them useful as a "good" development tool (instead of a fill-in that everyone's ashamed of when using it). (After all, the Lisp languages seem to embrace macros.)

Please Note: This is not about #include or #pragma or #ifdef. This is about #define MY_MACRO(...) ...

Note: I do not intend for this question to be subjective. Should you think it is, feel free to vote to move it to programmers.SE.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

红衣飘飘貌似仙 2024-12-06 21:32:01

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

显然,在某些极端情况下它们是有用的,但大多数时候,这是来自 C 世界的坏习惯,被那些相信有一种称为 C/C++ 的语言的人应用到 C++ 中,

所以说“它是邪恶的”比让开发人员冒险更容易滥用它们。

There a many uses where a C developper uses Macros and an C++ developper uses templates.

There obviously corner cases where they're useful, but most of the time it's bad habits from the C world applied to C++ by people that believe there such a language called C/C++

So it's easier to say "it's evil" than risking a developper misuses them.

话少情深 2024-12-06 21:32:01
  1. 宏不提供类型安全
  2. 参数执行两次的问题,例如 #define MAX(a,b) ((a)>(b) ? (a) : (b)) 并将其应用于 MAX(i++, y-- )
  3. 调试时出现问题,因为它们的名称不会出现在符号表中。
  1. Macros do not offer type safety
  2. Problems where parameters are executed twice e.g. #define MAX(a,b) ((a)>(b) ? (a) : (b)) and apply it for MAX(i++, y--)
  3. Problems with debugging as their names do not occur in the symbol table.
枫林﹌晚霞¤ 2024-12-06 21:32:01

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

Forcing the programmer to use proper naming for the macros... and better tools to track replacement of macros would fix most my problems. I can't really say I've had major issues so far... It's something you burn yourself with and learn to take special care later on. But they badly need better integration with IDEs, debuggers.

秋意浓 2024-12-06 21:32:00

宏被广泛认为是邪恶的,因为预处理器是一个愚蠢的文本替换工具,对 C/C++ 知之甚少。

宏邪恶的四个很好的理由可以在 C++ FAQ Lite

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

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

Macros are widely considered evil because the preprocessor is a stupid text replacement tool that has little to none knowledge of C/C++.

Four very good reasons why macros are evil can be found in the C++ FAQ Lite.

Where possible, templates and inline functions are a better choice. The only reason I can think of why C++ still needs the preprocessor is for #includes and comment removal.

A widely disputed advantage is to use it to reduce code repetition; but as you can see by the boost preprocessor library, much effort has to be put to abuse the preprocessor for simple logic such as loops, leading to ugly syntax. In my opinion, it is a better idea to write scripts in a real high-level programming language for code generation instead of using the preprocessor.

甜中书 2024-12-06 21:32:00

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

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

由于预处理器很好地集成到 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++ 函数,但事实并非如此。这就是导致滥用的地方。它只是生成一个 C++ 表达式,而函数是更好的方法。

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

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

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

Most preprocessor abuse come from misunderstanding, to quote Paul Mensonides(the author of the Boost.Preprocessor library):

Virtually all
issues related to the misuse of the preprocessor stems from attempting to
make object-like macros look like constant variables and function-like
macro invocations look like underlying-language function calls. At best,
the correlation between function-like macro invocations and function calls
should be incidental. It should never be considered to be a goal. That
is a fundamentally broken mentality.

As the preprocessor is well integrated into C++, its easier to blur the line, and most people don't see a difference. For example, ask someone to write a macro to add two numbers together, most people will write something like this:

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

This is completely wrong. Runs this through the preprocessor:

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

But the answer should be 3, since adding 1 to 2 is 3. Yet instead a macro is written to generate a C++ expression. Not only that, it could be thought of as a C++ function, but its not. This is where it leads to abuse. Its just generating a C++ expression, and a function is a much better way to go.

Furthermore, macros don't work like functions at all. The preprocessor works through a process of scanning and expanding macros, which is very different than using a call stack to call functions.

There are times it can be acceptable for macros to generate C++ code, as long as it isn't blurring the lines. Just like if you were to use python as a preprocessor to generate code, the preprocessor can do the same, and has the advantage that it doesn't need an extra build step.

Also, the preprocessor can be used with DSLs, like here and here, but these DSLs have a predefined grammar in the preprocessor, that it uses to generate C++ code. Its not really blurring the lines since it uses a different grammar.

生来就爱笑 2024-12-06 21:32:00

宏有一个显着的特点——它们很容易被滥用,但很难调试。您可以使用宏编写任何内容,然后宏就会扩展为单行代码,当没有任何效果时,您会非常困难调试生成的代码。

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

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

Macros have one notable feature - they are very easy to abuse and rather hard to debug. You can write just about anything with macros, then macros are expanded into one-liners and when nothing works you have very hard time debugging the resulting code.

The feature alone makes one think ten times on whether and how to use macros for their task.

And don't forget that macros are expanded before actual compilation, so they automatically ignore namespaces, scopes, type safety and a ton of other things.

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

另一件事是如果不小心使用,它们会导致代码非常难以阅读,因为没有人可以轻松地看到宏的计算结果,特别是当嵌套多个宏时,

一个好的指导原则是选择唯一的名称,并且在生成样板代码时,#尽快取消它们,以免污染 例如

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

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

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

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

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

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

The most important thing about macros is that they have no scope, and do not care about context. They are almost a dump text replacement tool. So when you #define max(.... then everywhere where you have a max it gets replaced; so if someone adds overly generic macro names in their headers, they tend to influence code that they were not intended to.

Another thing is that when used without care, they lead to quite hard to read code, since no one can easily see what the macro could evaluate to, especially when multiple macros are nested.

A good guideline is to choose unique names, and when generating boilerplate code, #undef them as soon as possible to not pollute the namespace.

Additionally, they do not offer type safety or overloading.

Sometimes macros are arguably a good tool to generate boilerplate code, like with the help of boost.pp you could create a macro that helps you creating enums like:

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

which could expand to

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

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

Things like assert() that need to react on NDEBUG are also often easier to implement as macros

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文