我正在与Elixir一起工作...我发现,在定义以下不带宏的代码时,我不得不放置inspect __ENV__
并从打印的行号中减去2以得到{{1} }正在运行。
print_block
print_block的函数定义如下:
h1 do
h2 do
print_block "prints message" do
IO.puts "hello world!"
IO.inspect(__ENV__)
end
end
end
类似的代码定义了h1和h2块。
现在,如果我改为将def print_block(_msg, do: code_block), do: code_block
,h1
和h2
定义为宏,则可以通过以下方式在现有文件中获取print_block
的确切行号:我检查print_block
时产生的AST。
code_block
因为Elixir元编程使您可以检查AST,所以唯一地精确匹配print_block的行号而不求助于“减去某些数字”的唯一方法是通过宏对其进行定义。使用传统的运行时模块/功能无法做到这一点,对吗?可以肯定的是,只是想确认一下。
答案 0 :(得分:1)
因为Elixir元编程可以让您检查AST [...]
情况并非完全如此。宏的作用是,在编译发生之前,它将注入 AST代替调用。编译后的代码没有任何宏的痕迹,它们都消失了,由在编译阶段返回的AST代替。
这意味着您的宏无法按预期工作。 IO.inspect
在编译阶段执行。 BEAM不会打印任何内容,也不会有__ENV__.line
或类似内容。
OTOH,Kernel.SpecialForms.quote/2
具有专门用于保存文件和行号的location: :keep
选项。开箱即用不会自动发生。
此外,还有一个@compile
模块属性,可以将其放入模块中以指定应内联哪些函数;基本上内联与调用宏几乎一样。
我无法理解您要追求的确切目标是什么,但是假设宏可以神奇地帮助您确定确切的行号是完全错误的。
以下是从__ENV__
获取行的所有可能性的说明:
bat /tmp/foo.ex
───────┬───────────────────────────────────────────────────────────────────
│ File: /tmp/foo.ex
───────┼───────────────────────────────────────────────────────────────────
1 │ defmodule Foo do
2 │ @compile {:inline, print_block_inline: 2}
3 │
4 │ def print_block_inline(_msg, do: code_block), do: code_block
5 │ def print_block_normal(_msg, do: code_block), do: code_block
6 │ defmacro print_block_macro(_msg, do: code_block), do: code_block
7 │
8 │ def test do
9 │ print_block_normal "prints message" do
10 │ IO.puts("hello world!")
11 │ IO.inspect(__ENV__.line, label: "11")
12 │ end
13 │
14 │ print_block_inline "prints message" do
15 │ IO.puts("hello world!")
16 │ IO.inspect(__ENV__.line, label: "16")
17 │ end
18 │
19 │ print_block_macro "prints message" do
20 │ IO.puts("hello world!")
21 │ IO.inspect(__ENV__.line, label: "21")
22 │ end
23 │ end
24 │ end
25 │
26 │ Foo.test()
所有内容均已成功打印。
elixir /tmp/foo.ex
hello world!
11: 11
hello world!
16: 16
hello world!
21: 21