2

我正在尝试编写一个宏来包装一个函数并从将为其调用分配的值中扣除一个参数。

object TestMacros {
  def foo(name: String): String = name.toUpper
  def bar = macro barImpl
  def barImpl(c: Context): c.Expr[String] = {
    import c.universe._
    //TODO extract value name (should be baz)
    c.Expr[String](Apply(
      Select(newTermName("TestMacros"), newTermName("foo")), // Probably wrong, just typed it quickly for demonstration purposes
      List(Literal(Constant("test"))))) // Should replace test by value name
  }
}

object TestUsage {
  val baz = bar // should be BAZ
}

我不知道这是否足够清楚。我已经调查了 c.prefix 和 c.macroApplication 都没有成功。我正在使用没有宏天堂编译器插件的 Scala 2.10.2。

4

2 回答 2

5

这是很有可能的。我知道,因为我以前做过类似的事情。诀窍是在封闭树中搜索其右侧与宏应用程序具有相同位置的值:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object TestMacros {
  def foo(name: String): String = name.toUpperCase

  def bar = macro barImpl
  def barImpl(c: Context): c.Expr[String] = {
    import c.universe._

    c.enclosingClass.collect {
      case ValDef(_, name, _, rhs)
        if rhs.pos == c.macroApplication.pos => c.literal(foo(name.decoded))
    }.headOption.getOrElse(
      c.abort(c.enclosingPosition, "Not a valid application.")
    )
  }
}

进而:

scala> object TestUsage { val baz = TestMacros.bar }
defined module TestUsage

scala> TestUsage.baz
res0: String = BAZ

scala> class TestClassUsage { val zab = TestMacros.bar }
defined class TestClassUsage

scala> (new TestClassUsage).zab
res1: String = ZAB

请注意,您可以foo在编译时申请,因为您知道val编译时的名称。当然,如果您希望它在运行时应用,那也是可能的。

于 2013-08-26T19:00:40.333 回答
3

当我想简化一些属性初始化时,我遇到了类似的问题。所以你的代码帮助我找出这是怎么可能的,但我收到了弃用警告。随着 scala 宏的发展,enclosingClass在 Scala 2.11 中已弃用。文档状态改为使用c.internal.enclosingOwner。quasiquotes 功能现在使事情变得更容易 - 我的示例仅检索名称,val baz = TestMacros.getName如下所示:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

object TestMacros {
  def getName(): String = macro getNameImpl
  def getNameImpl(c: Context)() = {
    import c.universe._
    val term = c.internal.enclosingOwner.asTerm
    val name = term.name.decodedName.toString
    // alternatively use term.fullName to get package+class+value
    c.Expr(q"${name}")
  }
}
于 2015-01-02T19:18:15.557 回答