为什么可变参数宏如此令人不快?
Why are variadic macros so unpleasant?
CPP 中的可变参数宏(C/C++ 预处理器;为了简单起见,我将在这个问题中将其视为一种单独的语言)非常与例如 C++ 的可变参数模板相比是有限的。本质上,可变参数宏只是带有参数的宏,其参数允许包含逗号。这没有提供直接的方法来计算参数、一个一个地对参数进行操作等。这些事情 可能 ,但需要精心设计、令人困惑且编译速度慢的 hack,例如那些this question 中描述。 直接 与 VA_ARGS
相关的唯一事情就是将它们传递给可变参数函数。
我的问题是,为什么要这样设计?在任何纯函数式语言(如 CPP)中,列表的标准方法是 cons 样式模式匹配:处理列表的第一个参数并递归其余参数,并为空列表提供基本情况。标准委员会成员应该非常熟悉这种方法。
为什么 CPP 的可变参数宏没有采用这样的方法?可变参数宏是否被视为一种简单的包装可变参数函数的方法,这样就不需要对参数列表进行操作了?是否有一些潜在的问题会导致可变参数宏递归变得不切实际?或者...?
注意:我不是在寻找 answers/comments 形式的 "because people shouldn't want variadic macros"。 boost.preprocessor 之类的东西的存在表明有理智的人希望以非平凡的方式使用预处理器。也不寻求关于为什么其他设计会成为 good/bad 想法的个人意见。我正试图找出当时的真正原因。
从 C 继承到 C++ 的 Variardic 宏。
Clive Feather 撰写了向 C 添加可变参数宏的论文。
The paper, N580: Varargs for Function-like Macros 状态:
This proposal allows the author of a macro to state that it takes a
variable number of arguments, and to substitute the trailing arguments
en bloc.
[...]
It would be possible to provide further facilities, but this has been left
for further proposals.
One issue is whether it should be possible to allow zero actual arguments
to match the trailing parameter. It has been decided not to allow this, as
this fits better with another proposal being made separately.
所以计划是添加最需要的功能(一个可用于填充 printf 调用的宏),并将进一步扩展留给以后的论文。
在 C++ 中,宏是第二 class 公民。 C++ 很少用新的宏语法或特性进行创新,而是想办法在没有宏的情况下解决问题。
C没有进一步创新
添加到 C 中的实际 vararg 宏来自那篇论文的派生,但那篇论文在这个决定背后有最清晰的动机文本。
显然,其意图似乎只是成为可变函数的包装器。 Variadic 宏在 C99 中引入,然后在 C++11 中引入。 C99 基本原理 V5.10 6.10.3 提供了这样的解释:
A new feature of C99: C89 introduced a standard mechanism for defining functions with
variable numbers of arguments, but did not allow any way of writing macros with the same
property. For example, there is no way to write a macro that looks like a call to printf.
This facility is now available. The macro definition uses an ellipsis in the same way to indicate a
variable argument list. However, since macro substitution is textual rather than run-time, a
different mechanism is used to indicate where to substitute the arguments: the identifier
__VA_ARGS__
. This is replaced by all the arguments that match the ellipsis, including the commas between them.
虽然 __VA_ARGS__
还有一些其他用途,但这可能更像是巧合。例如,您可以使用可变参数宏来计算初始化列表中存在的项目数。
已经有几个很好的答案,但我想对它们进行综合,并提到 user694733 的一个重要观点。鉴于提案文件和语言标准中的描述,该提案 的唯一设想用例 似乎确实委托给了 printf 样式的可变参数函数。
提案提到允许零实参"fits better with another proposal"。我一开始以为是"Not allowing zero arguments allows this proposal to be compatible with another one",但实际上这个意思似乎更接近"Allowing zero arguments should be part of a different proposal"。虽然我找不到可以参考的提案,但我确实找到了 a public comment by him,建议使用 __VA_COUNT__
关键字。假设该评论至少代表了他的其他提案的一部分,__VA_COUNT__
是人们想要更精细地使用可变参数宏的那种东西。
N580(及其后继者)似乎代表了允许可变参数通过宏传递的流行且无争议的提议,而 __VA_COUNT__
是更丰富的可变参数预处理的组成部分(通过 MACRO_ARGS_1
,MACRO_ARGS_2
,等等,或者通过其他一些未知的机制),只有第一个提案达成共识。
CPP 中的可变参数宏(C/C++ 预处理器;为了简单起见,我将在这个问题中将其视为一种单独的语言)非常与例如 C++ 的可变参数模板相比是有限的。本质上,可变参数宏只是带有参数的宏,其参数允许包含逗号。这没有提供直接的方法来计算参数、一个一个地对参数进行操作等。这些事情 可能 ,但需要精心设计、令人困惑且编译速度慢的 hack,例如那些this question 中描述。 直接 与 VA_ARGS
相关的唯一事情就是将它们传递给可变参数函数。
我的问题是,为什么要这样设计?在任何纯函数式语言(如 CPP)中,列表的标准方法是 cons 样式模式匹配:处理列表的第一个参数并递归其余参数,并为空列表提供基本情况。标准委员会成员应该非常熟悉这种方法。
为什么 CPP 的可变参数宏没有采用这样的方法?可变参数宏是否被视为一种简单的包装可变参数函数的方法,这样就不需要对参数列表进行操作了?是否有一些潜在的问题会导致可变参数宏递归变得不切实际?或者...?
注意:我不是在寻找 answers/comments 形式的 "because people shouldn't want variadic macros"。 boost.preprocessor 之类的东西的存在表明有理智的人希望以非平凡的方式使用预处理器。也不寻求关于为什么其他设计会成为 good/bad 想法的个人意见。我正试图找出当时的真正原因。
从 C 继承到 C++ 的 Variardic 宏。
Clive Feather 撰写了向 C 添加可变参数宏的论文。
The paper, N580: Varargs for Function-like Macros 状态:
This proposal allows the author of a macro to state that it takes a variable number of arguments, and to substitute the trailing arguments en bloc.
[...]
It would be possible to provide further facilities, but this has been left for further proposals.
One issue is whether it should be possible to allow zero actual arguments to match the trailing parameter. It has been decided not to allow this, as this fits better with another proposal being made separately.
所以计划是添加最需要的功能(一个可用于填充 printf 调用的宏),并将进一步扩展留给以后的论文。
在 C++ 中,宏是第二 class 公民。 C++ 很少用新的宏语法或特性进行创新,而是想办法在没有宏的情况下解决问题。
C没有进一步创新
添加到 C 中的实际 vararg 宏来自那篇论文的派生,但那篇论文在这个决定背后有最清晰的动机文本。
显然,其意图似乎只是成为可变函数的包装器。 Variadic 宏在 C99 中引入,然后在 C++11 中引入。 C99 基本原理 V5.10 6.10.3 提供了这样的解释:
A new feature of C99: C89 introduced a standard mechanism for defining functions with variable numbers of arguments, but did not allow any way of writing macros with the same property. For example, there is no way to write a macro that looks like a call to printf.
This facility is now available. The macro definition uses an ellipsis in the same way to indicate a variable argument list. However, since macro substitution is textual rather than run-time, a different mechanism is used to indicate where to substitute the arguments: the identifier
__VA_ARGS__
. This is replaced by all the arguments that match the ellipsis, including the commas between them.
虽然 __VA_ARGS__
还有一些其他用途,但这可能更像是巧合。例如,您可以使用可变参数宏来计算初始化列表中存在的项目数。
已经有几个很好的答案,但我想对它们进行综合,并提到 user694733 的一个重要观点。鉴于提案文件和语言标准中的描述,该提案 的唯一设想用例 似乎确实委托给了 printf 样式的可变参数函数。
提案提到允许零实参"fits better with another proposal"。我一开始以为是"Not allowing zero arguments allows this proposal to be compatible with another one",但实际上这个意思似乎更接近"Allowing zero arguments should be part of a different proposal"。虽然我找不到可以参考的提案,但我确实找到了 a public comment by him,建议使用 __VA_COUNT__
关键字。假设该评论至少代表了他的其他提案的一部分,__VA_COUNT__
是人们想要更精细地使用可变参数宏的那种东西。
N580(及其后继者)似乎代表了允许可变参数通过宏传递的流行且无争议的提议,而 __VA_COUNT__
是更丰富的可变参数预处理的组成部分(通过 MACRO_ARGS_1
,MACRO_ARGS_2
,等等,或者通过其他一些未知的机制),只有第一个提案达成共识。