在这种情况下,宏不是函数(-define(sub(X, Y), X-Y),它比 -define(sub(X, Y), (X-Y)) 更安全)的一个明显优势是它可以用作保护,因为自定义函数调用是被禁止的。
在许多情况下,将函数定义为内联函数会更安全。
另一方面,还有其他有趣的情况,例如测试中的断言或快捷方式,您希望在最终位置保留一些本地上下文。
例如,假设我想对一个测试进行通用调用,其目标是“匹配给定模式并返回给定值,或者在 M 毫秒后失败”。
我不能用代码来通用,因为模式不是你可以随身携带的数据结构。但是,使用宏:
-define(wait_for(PAT, Timeout),
receive
PAT -> VAL
after Timeout ->
error(timeout)
end).
这个宏可以用作:
my_test() ->
Pid = start_whatever(),
%% ...
?wait_for({'EXIT', Pid, Reason}, 5000),
?assertMatch(shutdown, Reason).
通过这样做,我可以在一些测试中简化文本的形式,而不需要一堆嵌套,并且以一种函数无法实现的方式。
请注意,由 eunit 定义的断言本身正在使用函数宏,并且执行类似于
的操作
-define(assertMatch(PAT, TERM),
%% funs to avoid leaking bindings into parent scope
(fun() ->
try
PAT = TERM,
true
catch _:_ ->
error({assertion_failed, ?LINE, ...})
end
end)()).
这同样让您可以携带模式和绑定,并执行其他方式无法实现的精美形式。
在最后一种情况下,您会注意到我使用了?LINE 宏。这是宏的另一个优点:您可以保留有关调用站点的信息和位置,例如其模块名称、行号等。这在需要此类元数据时很有用,例如当您报告测试失败时。