如何在Elixir中的同一文件中加载其他模块之前加载模块?
我正在尝试实现Protobuf的一些特性,我有一个例子:一个模块需要在编译时调用另一个模块。它们在同一个文件中,很难确保它们的顺序如何在Elixir中的同一文件中加载其他模块之前加载模块?,elixir,Elixir,我正在尝试实现Protobuf的一些特性,我有一个例子:一个模块需要在编译时调用另一个模块。它们在同一个文件中,很难确保它们的顺序 defmodule FakeProtobuf do defmacro __using__(opts) do quote do # register_attribute fields and set fields @before_compile FakeProtobuf end end defmacro __bef
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
在这段代码中,一些与字段相关的宏细节被忽略,但其主要思想与我上面所说的一样。无法编译此代码,因为编译Foo时,即使在code时,Bar也不可用。请确保调用\u loaded(Bar)
。我之所以需要它,是因为我想在编译时而不是运行时运行一些代码来节省时间
如果在Foo之前或在另一个文件中定义了Bar,则它可以工作。但在protobuf生成的文件中很难确保这一点
还有什么办法可以解决这个问题吗?我只需在其他模块之前定义
Bar
,就可以编译您的代码
当编译器开始编译模块时,它不知道该模块中的定义
它开始编译文件,并在编译/1宏之前遇到您的\u。当扩展该宏时,它需要调用模块B
中的一个函数,而编译器还没有看到该函数,因此它会出错
** (UndefinedFunctionError) function Bar.default/0 is undefined (module Bar is not available)
为了解决这个问题,我们在定义FakeProtoBuf
模块之前移动Bar
模块。这样编译器就可以安全地调用该函数,因为Bar
将被完全编译
最后,编译器将编译Foo
,它将使用FakeProtoBuf
中的\uuuu1
执行,这将在编译之前注入,从而在FakeProtoBuf
模块中调用宏\uuuuu-before-compile\uuuu
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
我可以通过在其他模块之前定义Bar
来编译代码
当编译器开始编译模块时,它不知道该模块中的定义
它开始编译文件,并在编译/1
宏之前遇到您的\u。当扩展该宏时,它需要调用模块B
中的一个函数,而编译器还没有看到该函数,因此它会出错
** (UndefinedFunctionError) function Bar.default/0 is undefined (module Bar is not available)
为了解决这个问题,我们在定义FakeProtoBuf
模块之前移动Bar
模块。这样编译器就可以安全地调用该函数,因为Bar
将被完全编译
最后,编译器将编译Foo
,它将使用FakeProtoBuf
中的\uuuu1
执行,这将在编译之前注入,从而在FakeProtoBuf
模块中调用宏\uuuuu-before-compile\uuuu
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
我对编译顺序做得不多。要求条形码有帮助吗?我不知道它到底是做什么的,但就它对宏的作用而言,我认为它在这里会起作用。是的,require
是我从最近的代码实验中可以看出的唯一方法。确保加载的和require
,前者并不总是起作用。我对编译顺序做得不多。要求条形码有帮助吗?我不知道它到底是做什么的,但就它对宏的作用而言,我认为它在这里会起作用。是的,require
是我从最近对代码的实验中可以看出的唯一方法。确保加载了和require
,前者并不总是有效的,但问题是要保证模块的顺序有点困难,因为代码是生成的。修复生成代码的东西。或者将它们放在单独的文件中。但问题是,要保证模块的顺序有点困难,因为代码是生成的。老实说,这就是你的问题。修复生成代码的东西。或者把它们放在不同的文件中。