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) メソッド apply が 2 回定義されています 競合するシンボルは両方ともファイル 'core/src/main/scala/Test.scala' @evis に由来します ^

4

1 に答える 1

3

この問題は、コンパイラにとって、マクロ注釈によって生成されたコードが手動で記述されたコードと何ら変わらないという事実によって引き起こされます。例で提供されているマクロによって生成されたコードを手動で記述すると、まったく同じ二重定義エラーが発生するため、これはバグではなく、ケース クラス合成の制限です。残念ながら、ケース クラス合成は拡張可能ではないため、これを回避する必要があります。

私が提案する 1 つの回避策CASEは、クラスの mod からフラグを消去することです。そうすれば、マクロは生成するメンバーを完全に自由に選択できます。ただし、これは、ケース クラスが通常生成するすべてのコードをマクロが生成する必要があることを意味し、これはあまり快適ではありません。ここでのもう 1 つの注意点は、コンパイラがクラスに対するパターン マッチングをCASE特別に扱い、より効率的なコードを生成することです。そのため、そのようなエミュレーションでもパフォーマンスがいくらか失われます (損失はごくわずかであり、おそらく新しいScala 2.11 からの名前ベースのパターン マッチング メカニズム - ただし、これはテストする必要があります)。

于 2014-08-16T19:47:53.633 に答える