我有一个模式,基本上是一些样板代码,其中一部分在中间变化
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
如果我在我的宏中使用循环语句而被捕获,这对我的特定用例来说是一个可接受的权衡 .
edit: 当然,如果可以的话,我会使用一些功能 . 我在这个问题中使用的示例是简化的,并没有展示只能用于宏魔术的位 .
6 回答
您可以将代码块放入宏参数中,前提是它没有无防护的逗号 . 在您的示例中,参数中唯一的逗号被保护,因为它被括号括起来 .
请注意,只有括号保护逗号 . 括号(
[]
)和大括号({}
)没有 .作为替代方案,您可以考虑使用复合语句之前的宏,如下所示 . 其中一个优点是所有调试器仍然可以进入复合语句,而复合语句作为宏参数方法则不然 .
使用一些goto magic(是的,'goto'在某些情况下可能是邪恶的,但我们在C中有很少的选择),宏可以实现为:
请注意,禁用编译器优化时,这会对性能和占用空间产生很小的影响 . 此外,在调用end_stuff()函数时,调试器似乎会跳回到MY_MACRO行,这实际上并不可取 .
此外,您可能希望在新的块范围内使用宏,以避免使用'm'变量污染您的范围:
当然,在复合语句中使用'break'不在嵌套循环内会跳过'end_stuff()' . 为了允许那些破坏周围循环并仍然调用'end_stuff()',我认为你必须用复合语句包含一个开始标记和一个结束标记,如:
请注意,由于该方法中的“中断”,MY_MACRO_EXIT宏只能在循环或开关中使用 . 不在循环中时,可以使用更简单的实现:
我使用'condition'作为宏参数,但如果需要,您也可以将它直接嵌入宏中 .
您可以将代码块放入宏中,但必须警告您使用调试器会使调试变得更加困难 . 恕我直言最好只是编写一个函数或切断代码行 .
如何改变函数指针(以及可选的
inline
函数)?“可以吗?”可能意味着两件事:
它会起作用吗?答案通常是肯定的,但存在陷阱 . 一个,as rici mentioned,是一个无人看守的逗号 . 基本上,请记住宏扩展是一个复制和粘贴操作,预处理器不理解它复制和粘贴的代码 .
这是个好主意吗?我会说答案通常是否定的 . 它使您的代码难以理解且难以维护 . 在一些罕见的情况下,如果实施得当,这可能比替代方案更好,但这是例外 .
在回答你的问题之前“可以使用宏”我想知道为什么要将这段代码转换为宏 . 你想要获得的是什么?成本是多少?
如果您反复使用相同的代码块,最好将其转换为函数,也可以是内联函数,并将其保留给编译器以使其内联或不内联 .
如果遇到崩溃问题,调试宏是一项繁琐的工作 .