我有一个模式,基本上是一些样板代码,其中一部分在中间变化
if(condition){ struct Foo m = start_stuff(); { m.foo = bar(1,2); m.baz = 17; } //this part varies end_stuff(); }
是否可以使宏taht将该中间代码块作为参数?C中的宏扩展规则看起来非常复杂,所以我不确定将来是否会出现任何可能出现并且咬我的角落情况(特别是,我不明白如果我的代码如何分离宏参数有逗号).
#define MY_MACRO(typ, do_stuff) do { \ if(condition){ \ struct typ m = start_stuff(); \ do_stuff; \ end_stuff(); \ } \ }while(0) //usage MY_MACRO(Foo, { m.foo = bar(1,2); m.baz = 17; });
到目前为止,我设法想到的唯一一件事就是如果我在宏中使用循环语句break
而continue
被捕获,这对我的特定用例来说是一个可接受的权衡.
编辑:当然,如果可以的话,我会使用一些功能.我在这个问题中使用的示例是简化的,并没有展示只能用于宏魔术的位.
您可以将代码块放入宏参数中,前提是它没有无防护的逗号.在您的示例中,参数中唯一的逗号被保护,因为它被括号括起来.
请注意,只有括号保护逗号.括号([]
)和括号({}
)没有.
另外,您可以考虑使用复合语句之前的宏,如下所示。优点之一是,所有调试器仍将能够进入您的复合语句,而使用复合声明作为宏参数方法则不是这种情况。
//usage MY_MACRO(Foo, condition) { m.foo = bar(1,2); m.baz = 17; }
使用一些goto魔术(是的,在某些情况下'goto'可能是邪恶的,但是在C语言中我们很少有其他选择),可以将宏实现为:
#define CAT(prefix, suffix) prefix ## suffix #define _UNIQUE_LABEL(prefix, suffix) CAT(prefix, suffix) #define UNIQUE_LABEL(prefix) _UNIQUE_LABEL(prefix, __LINE__) #define MY_MACRO(typ, condition) if (condition) { \ struct typ m = start_stuff(); goto UNIQUE_LABEL(enter);} \ if (condition) while(1) if (1) {end_stuff(); break;} \ else UNIQUE_LABEL(enter):
请注意,禁用编译器优化时,这对性能和占用空间的影响很小。同样,调试器在运行调用end_stuff()函数时似乎会跳回到MY_MACRO行,这并不是真正希望的。
另外,您可能希望在新的块范围内使用该宏,以避免使用'm'变量污染范围:
{MY_MACRO(Foo, condition) { m.foo = bar(1,2); m.baz = 17; }}
当然,在复合语句的嵌套循环中不使用“ break”会跳过“ end_stuff()”。为了让那些人打破周围的循环并仍然调用'end_stuff()',我认为您必须在复合语句中加上一个开始标记和一个结束标记,如下所示:
#define MY_MACRO_START(typ, condition) if (condition) { \ struct typ m = start_stuff(); do { #define MY_MACRO_EXIT goto UNIQUE_LABEL(done);} while (0); \ end_stuff(); break; \ UNIQUE_LABEL(done): end_stuff();} MY_MACRO_START(foo, condition) { m.foo = bar(1,2); m.baz = 17; } MY_MACRO_END
注意,由于该方法的“中断”,MY_MACRO_EXIT宏仅在循环或开关内可用。当不在循环中时,可以使用更简单的实现:
#define MY_MACRO_EXIT_NOLOOP } while (0); end_stuff();}
我使用“ condition”作为宏参数,但是如果需要,您也可以将其直接嵌入宏中。