说我有一个特征Foo
我用初始值i
实例化
val foo = new Foo(6) // class Foo(i: Int)
我稍后拨打secondMethod
,然后拨打myMacro
foo.secondMethod(7) // def secondMethod(j: Int) = macro myMacro
那么,myMacro
如何找到i
(6)的初始值?
使用c.prefix
,c.eval(...)
等进行正常的编译反射后,我没有成功,而是找到了一个双项目解决方案:
项目B:
object CompilationB {
def resultB(x: Int, y: Int) = macro resultB_impl
def resultB_impl(c: Context)(x: c.Expr[Int], y: c.Expr[Int]) =
c.universe.reify(x.splice * y.splice)
}
项目A(取决于项目B):
trait Foo {
val i: Int
// Pass through `i` to compilation B:
def apply(y: Int) = CompilationB.resultB(i, y)
}
object CompilationA {
def makeFoo(x: Int): Foo = macro makeFoo_impl
def makeFoo_impl(c: Context)(x: c.Expr[Int]): c.Expr[Foo] =
c.universe.reify(new Foo {val i = x.splice})
}
我们可以创建Foo
并使用正常实例化或像i
这样的宏设置makeFoo
值。第二种方法允许我们在编译时在第一次编译中自定义Foo
,然后在第二次编译中进一步自定义其对输入的响应(在这种情况下为i
)!在某种程度上,我们获得“元元”功能(或“pataphysic” - 能力; - )
通常我们需要在范围内使用foo来内省i
(例如c.eval(...))。但是,通过在i
对象中保存Foo
值,我们可以随时访问它,我们可以在任何地方实例化Foo
:
object Test extends App {
import CompilationA._
// Normal instantiation
val foo1 = new Foo {val i = 7}
val r1 = foo1(6)
// Macro instantiation
val foo2 = makeFoo(7)
val r2 = foo2(6)
// "Curried" invocation
val r3 = makeFoo(6)(7)
println(s"Result 1 2 3: $r1 $r2 $r3")
assert((r1, r2, r3) ==(42, 42, 42))
}
我的问题
如果没有这种双重编译hackery,我可以在我的示例宏中找到i
吗?
答案 0 :(得分:1)
事实证明,在宏中访问Foo
的成员很容易,而不必采用双重编译。以下是我们的示例宏如何访问i
:
val i = c.Expr[Int](Select(c.prefix.tree, TermName("i")))
reify(i.splice * j.splice)
准确地说,i
这里实际上是Expr
,我们可以在reify 中拼接和使用数字值。
所以是的,可以在宏(process
)内部访问定义宏的对象(i
)的成员(Foo
)(Foo
)在单个编译中(由“已定义”表示我使用macro
关键字的位置):
object compilation {
def makeFoo(x: Int): Foo = macro makeFoo_impl
def makeFoo_impl(c: Context)(x: c.Expr[Int]): c.Expr[Foo] =
c.universe.reify(new Foo {val i = x.splice})
def process(c: Context)(j: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
// Foo.i accessed inside macro
val i = c.Expr[Int](Select(c.prefix.tree, TermName("i")))
reify(i.splice * j.splice)
}
}
trait Foo {
val i: Int
def apply(j: Int) = macro compilation.process
}
object Test extends App {
import compilation._
val foo1 = new Foo {val i = 6}
Console println foo1(7) // 42
val foo2 = makeFoo(6)
Console println foo2(7) // 42
Console println makeFoo(6)(7) // 42
}
我欠Francesco Bellomi / Eugene Burmako关于Scala用户列表{@ 3}}的问题/答案的解决方案。
作为旁注,我们不一定需要使用c.eval(...)
从某些无类型的Expr
获取实际值 [正确地说它就像那样?] Tree
。在大多数情况下,我们应该很好地将值包装在Expr
中,因为我们可以将用作值,将其拼接在reify中并执行所有操作那里用(splice-)值计算!