首先,宏定义的语法不需要(或允许)您为宏实现方法指定参数,因此您拥有的代码永远不应该编译,即使在实现中使用文字也是如此。请参阅下面的示例以了解正确的语法。
接下来,Expr.value
不幸的是一个谎言。考虑宏方法的参数是变量的情况。变量的值在编译时是不知道的(在一般情况下是不知道的),但是您正在尝试使用该值创建一个编译时文字常量。它只是从根本上不起作用。
您有几个选项,根据您要执行的操作,这些选项可能适用也可能不适用。例如,假设我们需要将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
表达式。