6

我正在尝试实现一个隐式实现器,如下所述:http: //docs.scala-lang.org/overviews/macros/implicits.html

我决定创建一个宏,将 case 类从和转换为Stringusing quasiquotes 用于原型设计。例如:

case class User(id: String, name: String)
val foo = User("testid", "foo")

转换foo为文本应该导致,"testid foo"反之亦然。

这是我创建的简单特征及其伴随对象:

trait TextConvertible[T] {
  def convertTo(obj: T): String
  def convertFrom(text: String): T
}

object TextConvertible {
  import language.experimental.macros
  import QuasiTest.materializeTextConvertible_impl
  implicit def materializeTextConvertible[T]: TextConvertible[T] = macro materializeTextConvertible_impl[T]
}

这是宏:

object QuasiTest {
  import reflect.macros._

  def materializeTextConvertible_impl[T: c.WeakTypeTag](c: Context): c.Expr[TextConvertible[T]] = {
    import c.universe._
    val tpe = weakTypeOf[T]

    val fields = tpe.declarations.collect {
      case field if field.isMethod && field.asMethod.isCaseAccessor => field.asMethod.accessed
    }

    val strConvertTo = fields.map {
      field => q"obj.$field"
    }.reduce[Tree] {
      case (acc, elem) => q"""$acc + " " + $elem"""
    }

    val strConvertFrom = fields.zipWithIndex map {
      case (field, index) => q"splitted($index)"
    }

    val quasi = q"""
      new TextConvertible[$tpe] {
        def convertTo(obj: $tpe) = $strConvertTo
        def convertFrom(text: String) = {
          val splitted = text.split(" ")
          new $tpe(..$strConvertFrom)
        }
      }
    """

    c.Expr[TextConvertible[T]](quasi)
  }
}

这会产生

{
  final class $anon extends TextConvertible[User] {
    def <init>() = {
      super.<init>();
      ()
    };
    def convertTo(obj: User) = obj.id.$plus(" ").$plus(obj.name);
    def convertFrom(text: String) = {
      val splitted = text.split(" ");
      new User(splitted(0), splitted(1))
    }
  };
  new $anon()
}

生成的代码看起来不错,但是value id in class User cannot be accessed in User在尝试使用宏时出现编译错误。

我怀疑我使用了错误的字段类型。我试过field.asMethod.accessed.name了,但它会导致(注意anddef convertTo(obj: User) = obj.id .$plus(" ").$plus(obj.name );之后的多余空格),这自然会导致 error 。idnamevalue id is not a member of User

我究竟做错了什么?

4

2 回答 2

2

你得到的领域accessed.name附加了一个特殊的后缀,以避免命名冲突。

特殊的后缀是scala.reflect.api.StandardNames$TermNamesApi.LOCAL_SUFFIX_STRING,它的值,你猜对了,是一个空格字符。

当然,这是非常邪恶的。

于 2013-10-23T23:40:48.223 回答
2

啊,在发送我的问题后几乎立即想通了。

我改变了线条

val fields = tpe.declarations.collect {
  case field if field.isMethod && field.asMethod.isCaseAccessor => field.asMethod.accessed
}

val fields = tpe.declarations.collect {
  case field if field.isMethod && field.asMethod.isCaseAccessor => field.name
}

这解决了这个问题。

于 2013-10-23T14:43:51.987 回答