首页 > 解决方案 > 如何在 C 中实现 Go 的 defer() 以便允许声明变量?

问题描述

标签: cgccc-preprocessorc11gcc-extensions

解决方案


正如我在评论中所说,我的建议是首先避免使用这样的东西。无论您的视频说了什么或暗示了什么,现代 C 程序员的普遍观点是应该尽量减少宏的使用。类变量宏通常应该表示与上下文无关的常量值,类函数宏通常更好地实现为实际函数。这并不是说必须避免所有宏的使用,但是大多数现代 C 专业人士在复杂宏上看起来很糟糕,而且您defer()的复杂性足以满足要求。

此外,尝试将其他语言的风格和习语导入 C 对您自己没有任何好处。每种语言的常用习语之所以成立,是因为它们适用于该语言,而不是通常因为它们具有内在的内在价值。我建议你学习 C 和 C 程序员使用的习语,而不是如何编写看起来像 Go 的 C 代码。

话虽如此,让我们考虑一下您的defer()宏。你写,

然而问题是不能通过代码来启动声明新变量的代码段

,但实际上限制比这更强。因为宏start在逗号表达式 ( start,0) 中使用参数,所以它本身必须是表达式。不允许任何形式的声明或完整陈述。for这仅与出现在语句控制块的第一个子句中的表达式间接相关。(这同样适用于end论点。)

还可能需要注意的是,如果关联语句的执行通过通过or语句end从块分支出来或通过执行不返回的函数(例如or )终止,则宏扩展为无法评估表达式的代码可能也很重要。此外,与 Go 不同的是,表达式是在提供的语句之后完整计算的——之前没有对它的任何部分进行计算,这可能会让 Go 程序员感到惊讶。这些也是下面介绍的选项的特征。returngotoexit()longjmp()deferend

如果您只想将startandend作为宏参数传递,并且您希望允许声明出现在 中start,那么您可以这样做:

// Option 1
#define defer(start,end) start; for( \
        int macro_var_line(done) = 0; \
        !done; \
        (macro_var_line(done) += 1), (end))

这从宏的替换文本start中的for语句移到可能出现任意 C 代码的位置。但是请注意,任何变量声明都将被限定在最里面的包含块中。

如果您想限制声明的范围,那么还有这个替代方案和变体,我发现它比原来的更直接:

// Option 2
#define defer(start, end, body) { start; body end; }

你会像这样使用它:

defer(FILE *f = fopen("log.txt","a+"), fclose(f), // argument list continues ...
    fprintf(f,"Some message, f=%p",f);
);

这有点适合您的特定示例,因为它假设主体是作为零个或多个完整语句的序列(可以包括块、流控制语句)给出的。如您所见,它还要求将主体作为宏参数传递,而不是出现在宏调用之后,但我认为这是一个优势,因为它有助于识别延迟代码的启动点。


推荐阅读