如何在Scala宏中将context.universe.Annotation转换为MyAnnotation

时间:2015-02-14 16:48:59

标签: scala macros annotations

我在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
  • 如何将其MyAnnotationid类型的参数id进行比较

1 个答案:

答案 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")
      }
    }
  }
}