1

这个最小示例的调用代码似乎可以编译(Eclipse Indigo SR2,Scala v2.10.20),但包含调用代码的项目标有红叉(我不清楚如何对此进行进一步诊断)。不生成类文件。如果我用一些文字(例如 1)替换 param.value,则调用代码会编译。

这是一个已知问题吗?有解决方法吗?

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不幸的是一个谎言。考虑宏方法的参数是变量的情况。变量的值在编译时是不知道的(在一般情况下是不知道的),但是您正在尝试使用该值创建一个编译时文字常量。它只是从根本上不起作用。

您有几个选项,根据您要执行的操作,这些选项可能适用也可能不适用。例如,假设我们需要将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 回答