0

Foo初期値でインスタンス化する特性があるとしますi

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

後で a を呼び出し、secondMethodそれが次に呼び出すmyMacro

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

では、 (6)myMacroの初期値はどのように求めることができるでしょうか?i

c.prefixなどを使用した通常のコンパイル リフレクションでは成功しませんでしたc.eval(...)が、代わりに 2 つのプロジェクトの解決策を見つけました。

プロジェクト 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て値を設定できます。2 番目のアプローチでは、最初のコンパイルでコンパイル時に a をカスタマイズし、次に 2 番目のコンパイルで入力への応答をさらにカスタマイズできます (この場合)。何らかの方法で、「メタメタ」機能 (または「空想的」機能 ;-) を取得します。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は実際にExprreify 内で数値としてスプライスして操作できるです。

そうです、マクロ ( ) 内で、マクロが定義されているオブジェクト ( ) のprocessメンバー ( ) にアクセスすることが可能です (「定義済み」とは、キーワードを使用する場所を意味します)。iFooFoomacro

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
}

この解決策は、ここで見つけた Scala ユーザー リストの Francesco Bellomi/Eugene Burmako による質問/回答のおかげです。

補足として、型指定されていないものから実際の値c.eval(...)を取得するために必ずしも使用する必要はありません[そのように言うのは正しいですか?] 。ほとんどの場合、値を reify 内でスプライスすることで値として使用し、そこにある (splice-)value を使用してすべての計算を実行できるため、値を an でラップしても問題ありません。ExprTreeExpr

于 2013-06-24T22:30:18.217 に答える