【问题标题】:How to load a module before another loads in the same file in Elixir?如何在 Elixir 的同一个文件中加载另一个模块之前加载一个模块?
【发布时间】:2019-03-05 08:49:01
【问题描述】:

我正在尝试实现 Protobuf 的一些功能,我有一个案例:一个模块需要在编译时调用另一个模块。它们在同一个文件中,很难确保它们的顺序。

defmodule FakeProtobuf do
  defmacro __using__(opts) do
    quote do
      # register_attribute fields and set fields
      @before_compile FakeProtobuf
    end
  end

  defmacro __before_compile__(_) do
    # result = Enum.map(fields, fn ...)

    # even Code.ensure_loaded doesn't work
    result = %{bar: Bar.default}
    quote do
      def default do
        unquote(Macro.escape(result))
      end
    end
  end
end

defmodule Foo do
  use FakeProtobuf
  # field :bar, type: Bar
end

defmodule Bar do
  def default do
    "bar"
  end
end

这段代码忽略了一些与字段相关的宏细节,但主要思想就像我上面说的一样。此代码无法编译,因为即使在调用 Code.ensure_loaded(Bar) 时,编译 Foo 时 Bar 也不可用。我需要这个,因为我想在编译时而不是运行时运行一些代码来节省一些时间。

如果 Bar 在 Foo 之前或在另一个文件中定义,它会起作用。但在 protobuf 生成的文件中很难确保这一点。

有没有办法解决这个问题?

【问题讨论】:

  • 我在编译顺序方面做得不多。 require Bar 会有帮助吗?我不知道它到底是做什么的,但它对宏的作用,我认为它会在这里工作。
  • 是的,就我最近对Code.ensure_loadedrequire 的实验来看,require 是唯一的方法,前者并不总是有效

标签: elixir


【解决方案1】:

我可以通过在其他模块之前简单地定义Bar 来编译您的代码。

当编译器开始编译您的模块FakeProtoBuf 时,它不知道该模块内的定义。

它开始编译文件并遇到您的__before__compile/1 宏。扩展该宏时,它需要调用模块 B 中的一个函数,编译器尚未看到该函数,因此会出错。

** (UndefinedFunctionError) function Bar.default/0 is undefined (module Bar is not available)

为了解决这个问题,我们将Bar 模块移到FakeProtoBuf 模块的定义之前。这样编译器可以安全地调用该函数,因为Bar 将被完全编译。

最后编译器将编译Foo,它将执行FakeProtoBuf中的__using__/1,这将注入@before_compile,从而调用FakeProtoBuf模块中的宏__before__compile__

  def default do
    "bar"
  end
end

defmodule FakeProtobuf do
  defmacro __using__(_opts) do
    quote do
      # register_attribute fields and set fields
      @before_compile FakeProtobuf
    end
  end

  defmacro __before_compile__(_) do
    result = %{bar: Bar.default()}

    quote do
      def default do
        unquote(Macro.escape(result))
      end
    end
  end
end

defmodule Foo do
  use FakeProtobuf
  # field :bar, type: Bar
end

【讨论】:

  • 但是问题是模块顺序有点难保证,因为代码是生成的。
  • 老实说,那是你的问题。修复生成代码的东西。或者将它们放在单独的文件中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多