我目前正在玩一些宏,但也许这是一个坏主意,但这是我的问题:
我有以下宏:
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B]
def usingImpl[A <: { def close(): Unit }, B](c: Context)(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {
import c.universe._
f.tree match {
case Function(params, body) =>
//val ValDef(modifiers, name, tpt, _) = params.head
c.Expr[B](
Block(
List(
//ValDef(modifiers, name, tpt, resource.tree)
ValDef(params.head.symbol, resource.tree)
),
body
)
)
case _: Select =>
reify {
val res = resource.splice
try {
f.splice(res)
} finally {
res.close()
}
}
}
}
如果是Select
,我只需调用该函数并关闭资源,工作正常。但是在Function
的情况下,我想将param值分配给资源并调用正文。当我使用ValDef
的已弃用创建者时,需要Symbol
和Tree
,一切正常。如果我使用的是外部注释的4-args创建者,则会收到编译器错误,指出值x$1
不在范围内。当我查看两个版本生成的代码时,它看起来完全相同:
Expr[Int]({
<synthetic> val x$1: Test.Foo = new Test.this.Foo();
x$1.bar.+(23)
})
有没有办法,只需使用params.head
并分配值?谢谢你的帮助!
修改:
我这样称呼宏:
object Test extends App {
import Macros._
class Foo {
def close() {}
def bar = 3
}
println(using(new Foo)(_.bar + 3))
}
正如我所说,如果我使用的是评论版本,它会给我一个编译错误,它会打印AST,最后会显示以下消息:[error] symbol value x$1 does not exist in Test$delayedInit$body.apply
我正在使用2.10.1。
答案 0 :(得分:7)
啊,现在我可以重现你的错误了。任何时候你得到“这条目似乎已经杀了编译器。”消息并在堆栈跟踪中看到这样的行:
at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
你的下一步应该是开始在c.resetAllAttrs
中包装内容。说实话,我第一次无法重现你的错误的原因是因为我在复制和粘贴代码之后用body
替换了块中的c.resetAllAttrs(body)
,甚至没有想到它。这只是一种反射。
请参阅示例this little single abstract method class demo,了解需要此技巧的地方的类似实例。
在你的情况下,如果我们有这个:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object Macros {
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) =
macro usingImpl[A, B]
def usingImpl[A <: { def close(): Unit }, B](c: Context)
(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {
import c.universe._
val expr = f.tree match {
case Function(ValDef(modifiers, name, tpt, _) :: Nil, body) =>
c.Expr[B](
Block(
ValDef(modifiers, name, tpt, resource.tree) :: Nil,
c.resetAllAttrs(body)
)
)
case _: Select => reify {
val res = resource.splice
try { f.splice(res) } finally { res.close() }
}
}
println(expr)
expr
}
}
编译测试代码时,我们会看到以下内容:
Expr[B]({
<synthetic> val x$1: $line3.$read.$iw.$iw.Test.Foo = new Test.this.Foo();
x$1.bar.$plus(3)
})
完全按照需要。