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