psp*_*int 3 c gcc c-preprocessor c11 gcc-extensions
在这个现代 C视频中,有一个技巧可以推迟代码的执行,直到块/作用域退出。它的使用方法如下:
\nint main()\n{\n int foo=0, bar;\n const char *etc = "Some code before defer";\n\n defer(profile_begin(), profile_end())\n {\n /* Some code, which will be automatically \n * preceded by call to profile_begin() and\n * followed by run of profile_end().*/\n foo++;\n bar = 1;\n }\n\n etc = "Some code after defer";\n foo = bar + 1;\n}\nRun Code Online (Sandbox Code Playgroud)\n视频中的实现:
\n#define macro_var_line(name) concat(name, __LINE__)\n#define defer(start,end) for( \\\n int macro_var_line(done) = (start,0); \\\n !macro_var_line(done); \\\n (macro_var_line(done) += 1), end)\nRun Code Online (Sandbox Code Playgroud)\n它的实现非常简单。可能令人困惑的是macro_var_line(name)宏。其目的是通过向临时变量添加当前行号(调用 defer 的位置)来简单地确保临时变量具有唯一的“混淆”名称。
然而问题是,无法将代码传递到声明新变量的代码片段,因为它被粘贴到使用类型(the )start的 for() 逗号运算符中。所以这是不可能的,例如:intint macro_var_line(done) = \xe2\x80\xa6
defer(FILE *f = fopen("log.txt","a+"), fclose(f))\n{\n fprintf(f,"Some message, f=%p",f);\n}\nRun Code Online (Sandbox Code Playgroud)\n我想要有这样的宏,能够在start代码片段中声明新的变量。它可以通过标准 C99、C11 或者某些 GCC 扩展来实现吗?
更新:我找到了一个利用 GCC 嵌套函数的解决方案。基本上,{ bblock }宏后面的那个defer()成为嵌套函数体。并且可以转发声明嵌套函数并从块之前调用它,即:
#define defer(start,end) \\\n auto void var_line(routine) (void); \\\n start; \\\n /* Invoke above predeclared void routine_123(void) function */ \\\n var_line(routine)(); \\\n end; \\\n /* Define the nested function */ \\ \n void var_line(routine) (void)\nRun Code Online (Sandbox Code Playgroud)\nUPDATE2:这是一个优雅的版本:
\nfor()在自己的/声明空间中运行第一个语句,if(cond == 0)通过检查/块启动正确运行块。#define defer(...) \\\n for (int var_line(cond) = 0; var_line(cond) == 0; ) \\\n for (FIRST_ARG(__VA_ARGS__); var_line(cond) == 0; ) \\\n for (SKIP_LAST_ARG(SKIP_FIRST_ARG(__VA_ARGS__)); \\\n var_line(cond) == 0; \\\n var_line(cond) += 1 ) \\\n for (int var_line(cond_int) = 0; \\\n var_line(cond_int) <= 1; \\\n var_line(cond_int) += 1 ) \\\n if (var_line(cond_int) == 1) \\\n { \\\n LAST_ARG(__VA_ARGS__); \\\n } else if (var_line(cond_int) == 0)\nRun Code Online (Sandbox Code Playgroud)\n
正如我在评论中所表达的,我的建议是首先避免使用这样的东西。无论您的视频说了或暗示了什么,现代 C 程序员的普遍观点是应尽量减少宏的使用。类似变量的宏通常应该表示与上下文无关的常量值,而类似函数的宏通常最好实现为实际函数。这并不是说必须避免所有宏的使用,但大多数现代 C 专业人士对复杂的宏的看法很差,而您的宏defer()足够复杂以符合资格。
此外,尝试将其他语言的风格和习语导入 C 语言对自己没有任何好处。每种语言的通用习语得以建立是因为它们适用于该语言,而不是通常因为它们具有固有的内在价值。我建议你学习 C 和 C 程序员使用的习惯用法,而不是如何编写看起来像 Go 的 C 代码。
话虽如此,让我们考虑一下您的defer()宏。你写,
然而问题是无法将代码传递到声明新变量的开始片段
,但实际上限制比这更强。由于宏start在逗号表达式 ( start,0) 中使用参数,因此它本身必须是表达式。不允许任何类型的声明或完整陈述。这仅与出现在for语句控制块的第一个子句中的表达式间接相关。(这同样适用于end论证。)
还需要注意的是,如果关联语句的执行通过or语句end分支出块或通过执行不返回的函数(例如or )而终止,则宏扩展为无法计算表达式的代码。此外,与 Go 不同的是,表达式在提供的语句之后进行完整计算——之前没有计算任何部分,这可能会让 Go 程序员感到惊讶。这些也是下面介绍的选项的特征。returngotoexit()longjmp()deferend
如果您只想传递start和end作为宏参数,并且希望允许声明出现在 中start,那么您可以这样做:
// Option 1
#define defer(start,end) start; for( \
int macro_var_line(done) = 0; \
!done; \
(macro_var_line(done) += 1), (end))
Run Code Online (Sandbox Code Playgroud)
这会start移出for宏替换文本中的语句,移动到可能出现任意 C 代码的位置。但请注意,任何变量声明的作用域都将限定在最里面的包含块。
如果您想限制声明的范围,那么还有这种替代方案及其变体,我发现它比原来的更简单:
// Option 2
#define defer(start, end, body) { start; body end; }
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
defer(FILE *f = fopen("log.txt","a+"), fclose(f), // argument list continues ...
fprintf(f,"Some message, f=%p",f);
);
Run Code Online (Sandbox Code Playgroud)
这在某种程度上适合您的特定示例,因为它假设主体作为零个或多个完整语句的序列给出(可以包括块、流控制语句等)。正如您所看到的,它还要求主体作为宏参数传递,而不是出现在宏调用之后,但我认为这是一个优点,因为它有助于识别延迟代码的启动点。
| 归档时间: |
|
| 查看次数: |
2382 次 |
| 最近记录: |