1

この最小限の例の呼び出しコードはコンパイルされているように見えますが (Eclipse Indigo SR2、Scala v2.10.20)、呼び出しコードを含むプロジェクトには赤いバツ印が付けられています (これをさらに診断する方法がわかりません)。クラス ファイルは生成されません。param.value を 1 などのリテラルに置き換えると、呼び出し元のコードがコンパイルされます。

これは既知の問題ですか? 回避策はありますか?

def myMacro( param : Int ): Int = macro myMacroImpl( param )

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
    import c.universe._

    c.Expr( Literal( Constant( param.value ) ) )         
}
4

1 に答える 1

4

まず第一に、マクロ定義の構文では、マクロ実装メソッドにパラメーターを指定する必要がない (または許可されない) ため、実装にリテラルが含まれていても、コードをコンパイルする必要はありません。正しい構文については、以下の例を参照してください。

Expr.valueは、残念ながら嘘です。マクロ メソッドの引数が変数の場合を考えてみましょう。変数の値はコンパイル時にわかりません (一般的にはわかりません) が、その値でコンパイル時のリテラル定数を作成しようとしています。根本的にうまくいかないだけです。

いくつかのオプションがあり、何をしようとしているのかによって、適用できる場合と適用できない場合があります。たとえば、値に 1 を追加しparamて結果を返す必要があるとします。コンパイル時に追加を行いたい場合は、コンパイル時にリテラルを取得できない場合に (コンパイル時に) 例外をスローする必要があります。

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  val p = param.tree match {
    case Literal(Constant(p: Int)) => p
    case _ => c.abort(c.enclosingPosition, "param must be a literal value!")
  }

  c.literal(p + 1) // Or, equivalently: c.Expr(Literal(Constant(p + 1))
}

param別のオプションは、式の周りにツリーを構築することです。たとえば、次も の後継を返しますparamが、追加は実行時に実行され、メソッドは非リテラル引数を処理できます。

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  c.Expr(
    Apply(Select(param.tree, newTermName("$plus")), c.literal(1).tree :: Nil)
  )
}

ただし、コンパイル時の加算と任意の式の両方を取得するつもりはありませんparam

于 2013-08-29T20:31:54.623 に答える