0

假设我有一个Foo用初始值实例化的特征i

val foo = new Foo(6) // class Foo(i: Int)

我后来打电话给一个secondMethod反过来打电话myMacro

foo.secondMethod(7) // def secondMethod(j: Int) = macro myMacro 

那么,怎样才能找到(6)myMacro的初值呢?i

我没有成功使用 等进行正常编译反射c.prefixc.eval(...)而是找到了一个 2-project 解决方案:

项目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并设置值。第二种方法允许我们在第一次编译时在编译时自定义一个,然后在第二次编译中进一步自定义它对输入的响应(在这种情况下)!在某种程度上,我们获得了“元元”能力(或“超形上学”能力;-)imakeFooFooi

通常我们需要 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))
}

我的问题

i如果没有这个双重编译黑客,我可以在我的示例宏中找到吗?

4

1 回答 1

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 用户列表中提出的问题/答案。

作为旁注,我们不一定需要使用从一些无类型的 [正确这样说?]c.eval(...)中获取实际值。在大多数情况下,我们应该很好地将值包装在 an 中,因为我们可以通过将其拼接到 reify 中并将其用作值,并使用那里的 (splice-) 值进行所有计算!ExprTreeExpr

于 2013-06-24T22:30:18.217 回答