1

似乎这不起作用(使用 2.11.1 和宏天堂 2.0.1)。我希望案例类生成的方法要么被抑制,要么在树中,这样我就可以摆脱它。这是一个硬限制吗?

class evis extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro EvisMacro.impl
}

object EvisMacro {

  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
    import c.universe._

    def makeApply(tpName: TypeName, parents: List[Tree], params: List[List[ValDef]] ) : List[Tree]= {
      List(q"""def apply(...$params): $tpName = null""")
    }

    val result = annottees map (_.tree) match {
      case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
        :: Nil if mods.hasFlag(Flag.CASE) =>
        c.info(c.enclosingPosition,  s"Eviscerating $tpname !($mods, $parents, $paramss)", true)

        parents match {
          case q"${pname: TypeName}" :: rest =>
            c.info(c.enclosingPosition, s"${pname.decodedName}", true )
            val sc = c.universe.rootMirror.staticClass( pname.decodedName.toString  )
            c.info(c.enclosingPosition, s"${sc}", true )
        }

        val name = tpname.toTermName
        q"""
        $classDef
        object $name {
          ..${makeApply(tpname, parents, paramss)}
        }
        """
      case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
        :: q"object $objName {..$objDefs}"
        :: Nil if mods.hasFlag(Flag.CASE) =>
        q"""
        $classDef
         object $objName {
           ..${makeApply(tpname, parents, paramss)}
           ..$objDefs
         }
         """
      case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a case class")
    }
    c.Expr[Any](result)
  }

}

使用它:

trait Thing
@evis
case class Trade(id: Long, notional: Long, comment: String) extends Thing
@evis
case class Pop(name: String) extends Thing

object Pop{

}

object TestTrade extends App{

  val t = Trade (1, 1, "")
  val p : Pop = Pop("")

  println(t)

}

结果是:

错误:(2, 2) 方法应用定义了两次冲突符号,都源自文件 'core/src/main/scala/Test.scala' @evis ^

4

1 回答 1

3

问题是因为对于编译器来说,宏注解生成的代码与手工编写的代码没有任何区别。如果您手动编写示例中提供的宏生成的代码,您将得到完全相同的双重定义错误,因此这不是错误 - 这是案例类合成的限制。不幸的是,案例类综合是不可扩展的,因此需要解决这个问题。

我建议的一种解决方法是CASE从类的 mod 中删除标志,然后宏可以完全自由地选择要生成的成员。然而,这意味着宏必须生成案例类通常生成的所有代码,这不会很令人愉快。这里的另一个警告是,编译器CASE通过发出更有效的代码来特别对待类的模式匹配,所以这样的模拟也会失去一些性能(我认为这种损失将是微不足道的,甚至可能不存在新的Scala 2.11 中基于名称的模式匹配机制——但这需要进行测试)。

于 2014-08-16T19:47:53.633 回答