定义一个像 Python "with" 语句一样工作的 C 宏有什么缺点?

What are the downsides to defining a C macro that works like the Python "with" statement?

本文关键字:缺点 工作 什么 语句 一个 Python with 定义 一样      更新时间:2023-10-16

在摆弄了一些C预处理器之后,我想到了一种类似于python的控制结构的方法,定义如下:

#define with(var) for(int i##__LINE__=0;i##__LINE__<1;)for(var;i##__LINE__<1;++i##__LINE__)
示例用法:

#include <cstdio>
#include "FileClass.hpp"
#include "with.hpp"
int main(){
    with(FileClass file("test.txt")){
        printf("%sn",file.readlines().c_str());}
    return 0;}

这个想法是双嵌套的for循环有一个外部混淆的迭代变量,该变量在内部循环中增加一次以打破它。这会导致以下代码被执行一次,其中var在其作用域中。

这有什么缺点吗?如果我对迭代变量进行了足够的混淆,就几乎不会有名称冲突的可能性,它只使用标准的预处理器特性,似乎没有任何适得其反的可能性,并且非常容易理解。

这似乎好得令人难以置信——有什么原因没有在所有地方使用吗?

有什么原因没有在所有地方使用?

是的,c++不是Python,如果我正确理解你的代码,这是完全相同的:

{
  FileClass file("test.txt");
  printf("%sn", file.readlines().c_str());
}

那么,缺点是什么呢?不自然的语法,使用预处理器进行代码混淆,用更多的样板代码实现与上面相同的功能,以及统一使用c++。足够了吗?

c++有非常重要的值类型和基于作用域的堆栈变量确定性销毁的概念。这导致了非常重要的习惯用法,如SBRM(范围约束资源管理,也称为RAII)。

它在精神上类似于用c编写的原始Bourne shell中使用的宏。它们旨在提供类似于Algol 68的语法,Algol 68显然是Bourne首选的语言。

来自http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h:

的小样本
#define IF    if(
#define THEN  ){
#define ELSE  } else {
#define ELIF  } else if ( 
#define FI    ;}

结果往往是C程序员(他们必须熟悉您的宏以及C本身的语法)或Algol 68,或者您的情况下的Python程序员难以阅读的代码。

如果我读了一个使用with()宏的c++程序,如果没有(a)意识到with()是一个宏(宏通常都是大写的名字),(b)跟踪宏的定义,(C)破译由扩展宏产生的相当奇怪的C代码,我就无法真正理解它在做什么。这是假设我没有陷入这样的陷阱,认为它是一个特定于编译器的扩展,或者C有一个我不知道的with语句。

或者,如果我碰巧理解Python with语句,那么我仍然需要(a)意识到您的with()宏旨在模仿Python with语句,并且(b)相信您能够正确使用。

多年前,我认为这是:

#define ever ;;
...
for (ever) { ... }

非常聪明。我仍然认为这很聪明;但我不再认为聪明是一件好事。

这似乎好得令人难以置信——有什么理由不这样做吗使用无处不在?

当然,这是一个很好的结构,对你来说很好,但是团队中的其他人呢?未来的你——六个月后——不记得你写的那些可爱的宏了怎么办?

简而言之,像这样的语法练习在家里是很好的练习,但在协作环境中是很糟糕的,甚至对于将来你一个人维护的代码来说也是如此。坚持最佳实践,你的代码将更容易维护和理解。

这并不是说你不应该在家里做这种事情。做你喜欢的事,让你的大脑保持灵活。但是不要在生产中使用你的热身练习!

这真的是"with"吗?Python中"with"的最大优点是它与上下文管理器一起工作,上下文管理器负责自动关闭/释放/解锁/取消分配"with"语句中的变量。在c++中,有一些标准方法可以做到这一点。例如,auto_ptr将负责自动删除分配了new的指针。学习这些标准成语,然后再重新定义它们。

这样做的一个明显的缺点是没有人能够读懂你的面条式的python -in- c++代码。祝你在维护代码时好运。