これは、正確に述べられているようには見えません。クラスメンバでマクロ注釈を使用しても、クラス自体のツリーを操作することはできません。つまり、マクロ注釈を持つクラス内のメソッドに注釈を付けると、macroTransform(annottees: Any*)
が呼び出されますが、注釈だけがメソッドそのものになります。
私は2つの注釈を使って概念実証を行うことができました。明らかにクラスに注釈を付けるだけではうまくいきませんが、別の方法で考えることはできません。
あなたは必要があります
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
アイデアは、親クラスのマクロのアノテーションを使用すると、拡大したい方法を見つけることができるように、あなたは、この注釈で、各メソッドに注釈を付けることができ、あります。
class alias(aliases: String *) extends StaticAnnotation
次にマクロ:
// Annotate the containing class to expand aliased methods within
@compileTimeOnly("You must enable the macro paradise plugin.")
class aliased extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AliasMacroImpl.impl
}
object AliasMacroImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val result = annottees map (_.tree) match {
// Match a class, and expand.
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }") :: _ =>
val aliasedDefs = for {
q"@alias(..$aliases) def $tname[..$tparams](...$paramss): $tpt = $expr" <- stats
Literal(Constant(alias)) <- aliases
ident = TermName(alias.toString)
} yield {
val args = paramss map { paramList =>
paramList.map { case q"$_ val $param: $_ = $_" => q"$param" }
}
q"def $ident[..$tparams](...$paramss): $tpt = $tname(...$args)"
}
if(aliasedDefs.nonEmpty) {
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
..$aliasedDefs
}
"""
} else classDef
// Not a class.
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: not a class")
}
c.Expr[Any](result)
}
}
この実装は脆くなります覚えておいてください。注釈を調べて、最初のものがClassDef
であることを確認します。次に、@alias
で注釈を付けられたメソッドであるクラスのメンバーを検索し、複数のエイリアスツリーを作成してクラスにスプライスします。注釈付きメソッドがない場合は、単に元のクラスツリーを返します。つまり、重複するメソッド名は検出されず、修飾子は削除されます(コンパイラは注釈と修飾子を同時に一致させません)。
これは、コンパニオンオブジェクトを扱うために簡単に拡張することもできますが、コードを小さくするためにそれらを残しました。私が使用したマッチャーについては、quasiquotes syntax summaryを参照してください。コンパニオンオブジェクトを処理するには、result
の一致を修正してcase classDef :: objDef :: Nil
を処理し、ケースobjDef :: Nil
を処理する必要があります。使用の際に
:
@aliased
class Socket {
@alias("ask", "read")
def load(n: Int): Seq[Byte] = Seq(1, 2, 3).map(_.toByte)
}
scala> val socket = new Socket
socket: Socket = [email protected]
scala> socket.load(5)
res0: Seq[Byte] = List(1, 2, 3)
scala> socket.ask(5)
res1: Seq[Byte] = List(1, 2, 3)
scala> socket.read(5)
res2: Seq[Byte] = List(1, 2, 3)
また、複数のパラメータリストを扱うことができます。
@aliased
class Foo {
@alias("bar", "baz")
def test(a: Int, b: Int)(c: String) = a + b + c
}
scala> val foo = new Foo
foo: Foo = [email protected]
scala> foo.baz(1, 2)("4")
res0: String = 34
quasiquotesを使ってなんとかようです。 Macrosに関する最近のミートアップからこのスライドデッキをチェックしてください:https://speakerdeck.com/bwmcadams/ny-scala-meetup-scala-macros-or-how-i-learned-to-stop-worrying-and-mumble- wtf – JoseM