如何通过 Erlang 的预处理器有条件地排除整个子句?

rad*_*row 2 macros erlang preprocessor

我正在用 Erlang 编写一些应该在各种 OTP 版本上编译的模块。在某些情况下,我想使用旧版本中不受支持的++操作符模式,但前提是它可用。因此,我对这项任务的尝试如下:

f([]) -> empty;
f([_|_]) -> cons;
-if(?OTP_RELEASE >= 23).
f([] ++ _) -> empty_append;
-endif.
f(X) -> error(nah).
Run Code Online (Sandbox Code Playgroud)

我发现的这个问题的解决方法是:

  • 将 iffed 子句移到开头。然而,这在以下情况下效果不佳:

    • 我需要添加更多-ifs
    • 条款的顺序禁止我以任何理由这样做。
  • 创建一个美丽的 ifelses 花园,并多次复制整个函数。显然,这不会通过。

有什么方便的方法吗?如果可能的话,我希望有一个通用的解决方案,不限于所呈现的情况。

Jos*_*é M 5

来自宏中的流控制The macro directives cannot be used inside functions.所以如果你想使用-ifdef你需要多次复制相同的功能。

(我想这与它们被定义在-和之间有关.

话虽如此,您可以执行以下操作:

f([]) -> empty;
f([_|_]) -> cons;
f([] ++ _) when ?OTP_RELEASE >= 23 -> empty_append;
f(X) -> error(nah).
Run Code Online (Sandbox Code Playgroud)

您可以使用 验证从生成的梁 ASM 代码中删除永远不会匹配的子句erlc -S <module>。此外,编译器将显示警告。

在此步骤中产生的警告不能有选择地省略。从这里

Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as a=b), guards that always evaluate to false, and expressions that always fail (such as atom+42).

Those warnings cannot be disabled (except by disabling all warnings).
Run Code Online (Sandbox Code Playgroud)

如果你想完全避免警告,你需要类似的东西(请注意,这种风格不会删除分支,因为它OtpRelease是一个变量):

Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as a=b), guards that always evaluate to false, and expressions that always fail (such as atom+42).

Those warnings cannot be disabled (except by disabling all warnings).
Run Code Online (Sandbox Code Playgroud)

编辑:在最后一个示例中,OTP23 的代码优化器能够删除无法访问的代码。在这种特殊情况下,如果f/2不导出,则其第 3 个或第 4 个子句将被删除,具体取决于?OTP_RELEASE)