我希望编写一个宏
在 REPL 中,这个定义完全正常:
如果我将
我猜问题在于编译器尝试按照以下步骤进行:
这个问题的背景是,我正在基于迭代求解器的实现中工作,这类求解器的实现方式类似于https://gist.github.com/jiahao/9240888。像MinRes这样的算法需要相应状态对象中的许多变量(目前为8个),我既不想每次在
@unpack t
,它可以将对象t
的所有字段复制到本地作用域中。例如,给定以下代码:immutable Foo
i::Int
x::Float64
end
foo = Foo(42,pi)
表达式 @unpack foo
应该扩展为
i = foo.i
x = foo.x
不幸的是,这样的宏并不存在,因为它需要知道传递对象的类型。为了绕过这个限制,我引入了一个特定类型的宏@unpackFoo foo
,具有相同的效果,但由于我很懒,希望编译器为我编写@unpackFoo
。所以我将类型定义更改为
@unpackable immutable Foo
i::Int
x::Float64
end
应该扩展为
immutable Foo
i::Int
x::Float64
end
macro unpackFoo(t)
return esc(quote
i = $t.i
x = $t.x
end)
end
编写@unpackable
并不太难:
macro unpackable(expr)
if expr.head != :type
error("@unpackable must be applied on a type definition")
end
name = isa(expr.args[2], Expr) ? expr.args[2].args[1] : expr.args[2]
fields = Symbol[]
for bodyexpr in expr.args[3].args
if isa(bodyexpr,Expr) && bodyexpr.head == :(::)
push!(fields,bodyexpr.args[1])
elseif isa(bodyexpr,Symbol)
push!(fields,bodyexpr)
end
end
return esc(quote
$expr
macro $(symbol("unpack"*string(name)))(t)
return esc(Expr(:block, [:($f = $t.$f) for f in $fields]...))
end
end)
end
在 REPL 中,这个定义完全正常:
julia> @unpackable immutable Foo
i::Int
x::Float64
end
julia> macroexpand(:(@unpackFoo foo))
quote
i = foo.i
x = foo.x
end
如果我将
@unpackFoo
放在与@unpackable
相同的编译单元中,则会出现问题。julia> @eval begin
@unpackable immutable Foo
i::Int
x::Float64
end
foo = Foo(42,pi)
@unpackFoo foo
end
ERROR: UndefVarError: @unpackFoo not defined
我猜问题在于编译器尝试按照以下步骤进行:
- 展开
@unpackable
,但不解析它。 - 尝试展开
@unpackFoo
,但失败了,因为@unpackable
的扩展尚未被解析。 - 如果我们在第二步没有失败,那么编译器现在将解析
@unpackable
的扩展。
@unpackable
在源文件中的使用。有没有办法告诉编译器在上述列表中交换步骤2和3?
这个问题的背景是,我正在基于迭代求解器的实现中工作,这类求解器的实现方式类似于https://gist.github.com/jiahao/9240888。像MinRes这样的算法需要相应状态对象中的许多变量(目前为8个),我既不想每次在
next()
函数中使用变量时都写state.variable
,也不想手动复制所有变量,因为这会使代码膨胀并且难以维护。最终,这主要是元编程的练习。
i,x =(42,pi)
)获得@unpack
行为。 - gTcVParameters
包。它有一个@unpack
宏和相应的机制,类似于您所需的建议。 - Dan Getz