检查 Erlang OTP Release 以在 Elixir 上执行不同的功能

Checking Erlang OTP Release to execute different functions on Elixir

我正在创建一个新的 Elixir 库,需要使用不同版本的语言执行。

此库使用 Erlang :crypto.hmac 函数,但此函数在 Erlang OTP 的 22 版中被 :crypto.mac 替换(具有相同的功能)。

我正在使用以下私有宏来执行较新或较旧的函数:

defmacrop hmac(key, data) do
  if System.otp_release() >= "22" do
    quote do: :crypto.mac(:hmac, :sha256, unquote(key), unquote(data))
  else
    quote do: :crypto.hmac(:sha256, unquote(key), unquote(data))
  end
end

并按以下方式使用它:

hmac(key, data)

两个问题:

  1. 这是基于 OTP 发布版本执行代码的正确方法吗?
  2. 有没有更明显的方法来解决这个问题?

谢谢。

宏是编译时的产物。编译后没有宏的踪迹;相反,它注入的 AST 发生在发布中。

也就是说,如果您希望它是一个编译时,这意味着您将需要许多环境来编译,并且每个版本都只能在编译时在同一平台上运行,这是正确的方法。

如果你想 assemble 一次发布并使其在不同的目标平台上工作,你最好使用 Kernel.apply/3 as

def hmac(key, data) do
  if String.to_integer(System.otp_release) >= 22,
    do: apply(:crypto, :mac, [:hmac, :sha256, key, data]),
    else: apply(:crypto, :hmac, [:sha256, key, data])
end

旁注:即使你希望它是编译时的,声明不同的函数而不是宏会更干净。

if String.to_integer(System.otp_release) >= 22 do
  def hmac(key, data),
    do: :crypto.mac(:hmac, :sha256, key, data)
else
  def hmac(key, data),
    do: :crypto.hmac(:sha256, key, data)
end

这将在编译时进行评估,相应的功能将在生成的 BEAM 代码中发生。

Is this a correct way of execute code based in the OTP release version?

您不应检查 OTP 版本,而应检查应用程序(在本例中为 crypto)版本。由于 OTP 版本可能与应用程序版本不同。

Any better obvious way to address this problem?

是的,检查给定函数是否导出,而不是检查 OTP/application.

的版本

在运行时有两种方法:

def hmac(key, data) do
  if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do
    :crypto.mac(:mac, :sha256, key, data)
  else
    :crypto.hmac(:sha256, key, data)
  end
end

或在编译期间:

if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do
  def hmac(key, data), do: :crypto.mac(:mac, :sha256, key, data)
else
  def hmac(key, data), do: :crypto.hmac(:sha256, key, data)
end