Reputation: 15146
I'm writing a module A
which defines macroses and these macroses are used by another module B
defmodule A do
defmacro __using__(_) do
quote do
import A, only: [macross: 1]
def call
IO.inspect @macross_value
end
end
end
defmacro macross(value)
quote do
@macross_value value
end
end
end
defmodule B do
use A
macross 6
end
Unfortunately, B.call => nil
not a 6
. It's because when def call
is running macross
in module B
hasn't been runned yet. How could I fix it?
How could I make function on a module defined only if all macroses has been run via use
? Is there any more interesting solution?
Upvotes: 1
Views: 67
Reputation: 222128
You can use @before_compile
to define the functions after the module has completed its initial evaluation and is about to be compiled. In the __before_compile__/1
callback, you'll need to return an AST that defines the function.
defmodule A do
defmacro __using__(_) do
quote do
import A, only: [macross: 1]
@before_compile A
end
end
defmacro macross(value) do
quote do
@macross_value unquote(value)
end
end
defmacro __before_compile__(_) do
quote do
def call do
IO.inspect @macross_value
end
end
end
end
defmodule B do
use A
macross 6
end
B.call
6
This doesn't ensure that the macro has been called by the user at least once though. For that, you can wrap the def call
with a check that the module attribute is defined:
defmacro __before_compile__(_) do
quote do
if Module.get_attribute(__MODULE__, :macross_value) do
def call do
IO.inspect @macross_value
end
end
end
end
Now, if the attribute hasn't been defined, call/0
will not be defined on the module and you'll get:
** (UndefinedFunctionError) function B.call/0 is undefined or private
B.call()
(elixir) lib/code.ex:376: Code.require_file/2
Upvotes: 4