此最小示例的调用代码似乎是编译(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 ) ) )
}
答案 0 :(得分:3)
首先,宏定义的语法不需要(或允许)指定宏实现方法的参数,因此您的代码永远不应该编译,即使在实现中使用文字也是如此。请参阅下面的示例,了解正确的语法。
接下来,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
表达式。