我在Scala 2.11.1上,我有一个注释
case class MyAnnotation(id: String, message: String) extends StaticAnnotation
我想创建一个转换以下代码的宏MessageFromMyAnnotation
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = MessageFromMyAnnotation("001")
... // my messy code
}
}
到
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = "Hello world, James Bond 001"
... // my messy code
}
}
简而言之,宏在其封闭元素上找到message
@MyAnnotation
的{{1}}并返回id = "001"
这是宏
"Hello world, " + message
我想将object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
c.internal.enclosingOwner.annotations.filter( anno =>
anno.tpe =:= c.universe.typeOf[MyAnnotation] &&
anno.asInstanceOf[MyAnnotation].id == id.value //this does not work
) match {
case anno :: Nil => c.universe.reify("Hello world, " + ...)
case x => c.abort(c.enclosingPosition, c.universe.showRaw(x))
}
}
}
类型的anno
转换为cuniverse.Annotation
,并将其MyAnnotation
与类型id
的参数id
进行比较,但是c.Expr[String]
产生anno.asInstanceOf[MyAnnotation]
,ClassCastException
给我一条错误消息
id.value
所以,请帮我解决2个问题:
cannot use value except for signatures of macro implementations
类型的anno
转换为cuniverse.Annotation
MyAnnotation
与id
类型的参数id
进行比较答案 0 :(得分:0)
感谢@ Imm的建议,我成功地做到了这一点:
You don't have an instance of MyAnnotation - this is compile time, you only
have an AST that represents the call. You can get the Expr that's the
parameter given to the cuniverse.Annotation, and either splice it, or pattern
match it as a String literal and then take the value out of that.
这是代码
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
import c.universe._
id match { case Expr(Literal(Constant(idVal: String))) =>
(for { Annotation(tpe,
Literal(Constant(annoIdVal: String)) ::
Literal(Constant(annoMessageVal: String)) ::
Nil, _) <- c.internal.enclosingOwner.annotations
if tpe =:= typeOf[MyAnnotation] && idVal == annoIdVal
} yield (annoIdVal, annoMessageVal)
) match {
case (annoIdVal, annoMessageVal) :: Nil =>
reify(c.literal("Hello world, " + annoMessageVal).splice)
case matchedMore :: thanOne :: Nil => c.abort(c.enclosingPosition, "Found more than one @MyAnnotation with the same id")
case x => c.abort(c.enclosingPosition, "Not Found @MyAnnotation with the specified id")
}
}
}
}