How to protect from multiple calls to __using__ ?

87 views
Skip to first unread message

Sébastien Merle

unread,
Apr 18, 2015, 4:02:45 PM4/18/15
to elixir-l...@googlegroups.com
I have two modules B and C that both define a __using__ macro that quote use A.
Then I have a module D that uses both modules B and C.
Because the module A is generating some code in its __using__ macro I don't want it to generate the code multiple times.
I can solve it by doing the following in module A:

  defmacro __using__(_) do
    quote do
      if @module_a_defined == nil do
        @module_a_defined true
        # Generate the code here
      end
    end
  end


This is actually working but it generate and understandable warning:

  warning: undefined module attribute @module_a_defined, please remove access to @module_a_defined or explicitly set it to nil before access

Is there a clean way to do this ?

Is there a way to know if an attribute is already defined without generating a warning ?

If there is no such at thing, I would have expected something like Module.has_attribute?/2.


Thank you.

- Sebastien.

Sébastien Merle

unread,
Apr 18, 2015, 4:07:54 PM4/18/15
to elixir-l...@googlegroups.com
Just figured out that if you do the following you don't get any warnings:

  defmacro __using__(_) do
    quote do
      if Module.get_attribute(__MODULE__, :module_a_defined) == nil do

        @module_a_defined true
        # Generate the code here
      end
    end
  end

Still, is there a better way for doing this ?

Thank you.

- Sebastien.

Alex Shneyderman

unread,
Apr 21, 2015, 6:18:05 AM4/21/15
to elixir-l...@googlegroups.com
   defmacro __using__(_) do
        if !Module.get_attribute(__CALLER__.module, :generated) do
            Module.register_attribute(__CALLER__.module, :generated, [])
            Module.put_attribute(__CALLER__.module, :generated, true)
            quote do
                def test, do: IO.puts("test")
            end
        end
    end

You can register attributes, and assign values on the fly. You can also choose what module you register them in. In your case it is the caller's module that you want to avoid double definitions in, so you register and assign value to the attribute in that module. With the code above when module D gets to expand use C (it already ran use B) the attribute will already be on D and you can safely skip A's generated content.
Reply all
Reply to author
Forward
0 new messages