请原谅标题中任何令人困惑的术语,但想象一下我想要一个小宏来标记我创建的结构可用于某些恶意目的。我写了这个小模块:
module Usable
export @usable, isusable
isusable(::Type{T}) where T = false
macro usable(expr::Expr)
name = expr.args[2]
return quote
$expr
Usable.isusable(::Type{$name}) = true # This in't working
end
end
end
Run Code Online (Sandbox Code Playgroud)
但是,尝试使用我的宏
julia> include("Usable.jl")
Main.Usable
julia> using Main.Usable
julia> @usable struct Foo
bar::String
end
Run Code Online (Sandbox Code Playgroud)
结果是
ERROR: UndefVarError: Foo not defined
Run Code Online (Sandbox Code Playgroud)
该结构显然定义得很好
julia> Foo("soup")
Foo("soup")
Run Code Online (Sandbox Code Playgroud)
所以看起来这个定义比我预期的要早。我显然错过了一些东西,但我无法弄清楚是什么。
始终查看宏的输出:
julia> @macroexpand @usable struct Foo
bar::String
end
quote
#= REPL[1]:11 =#
struct Foo
#= REPL[4]:2 =#
bar::Main.Usable.String
end
#= REPL[1]:13 =#
(Main.Usable.Usable).isusable(::Main.Usable.Type{Main.Usable.Foo}) = begin
#= REPL[1]:13 =#
true
end
end
Run Code Online (Sandbox Code Playgroud)
问题是宏输出在它定义的模块内展开,这弄乱了名称的含义。在这种情况下,我们想要Foo引用定义它的命名空间,但是:
quote
#= REPL[1]:11 =#
struct Foo
#= REPL[10]:2 =#
bar::String
end
#= REPL[1]:13 =#
Usable.isusable(::Type{Foo}) = begin
#= REPL[1]:13 =#
true
end
end
Run Code Online (Sandbox Code Playgroud)
得到它实际上很简单 - 只需转义输出:
macro usable(expr::Expr)
name = expr.args[2]
return esc(quote
$expr
$Usable.isusable(::Type{$name}) = true
end)
end
Run Code Online (Sandbox Code Playgroud)
但是请再次阅读宏文档。 esc非常复杂,您不想盲目地将它应用到您编写的所有内容中。
另一件事(我希望能做到这一点)是拼接模块本身 -- $Usable-- 而不是按名称引用它。否则,如果在外部重命名模块名称,则可能会出现问题。