在Scala宏注释上键入参数

时间:2013-11-05 14:47:02

标签: reflection scala-macros

我正在尝试在scala中使用宏注释,其中我的宏注释将采用另一种类型的参数。然后它将使用scala反射来查看传入的类型,并根据需要添加一些方法.Eg。

trait MyTrait {
  def x: Int
  def y: Float
}

@MyAnnotation class MyClass //<-- somehow, this annotation should reference MyTrait

class MyAnnotation(val target: Any) extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro MyAnnotationImpl.impl
}
object MyAnnotationImpl {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    // if I can get a handle on the type MyTrait in here
    // then I can call .members on it, etc.
    ...
  }
}

基本上,与Using Scala reflection in Scala macros相同,除了使用宏注释。但是,当我尝试使用TypeTag

模板化我的宏注释时
class MyAnnotation[T](val target: Any) extends StaticAnnotation {
  def macroTransform[T](annottees: Any*) = macro MyAnnotationImpl.impl[T]
}
object MyAnnotationImpl {
  def impl[T: c.WeakTypeTag](c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    ...
  }
}

我得到了

[error] /Users/imran/other_projs/learn_macros/macros/src/main/scala/com/imranrashid/oleander/macros/MacrosWithReflection.scala:7: macro annotation has wrong shape:
[error]   required: def macroTransform(annottees: Any*) = macro ...
[error]   found   : def macroTransform[T](annottees: Any*) = macro ...
[error] class MyAnnotation[T](val target: Any) extends StaticAnnotation {
[error]       ^

我也尝试将类型作为我的注释的参数,所以我会像@MyAnnotation(MyTrait) class Foo ...一样使用它。我可以使用类似

的名称将名称提取为字符串
val targetTrait = c.prefix.tree match {
  case Apply(Select(New(Ident(_)), nme.CONSTRUCTOR), List(Ident(termName))) => termName
}

但是,我不知道我能做什么而且那个String能够取回完整的类型。我还尝试过类似@MyAnnotation(typeOf[MyTrait]) class Foo ...的变体,然后在我的宏内c.eval上使用typeOf,但这也不会编译。

2 个答案:

答案 0 :(得分:5)

在宏天堂2.0.0-SNAPSHOT中,我们有一种非常棘手的方法来访问宏注释的类型参数(当我们有专门的API时,情况将会改善,但是现在很难将新功能引入scala-reflect.jar在宏天堂,所以目前的API有点粗糙。)

现在有必要在注释类上指定type参数,而不是在macroTransform方法上声明任何类型参数。然后,在宏扩展中,访问c.macroApplication并提取与传递的类型参数对应的无类型树。然后,按照Can't access Parent's Members while dealing with Macro Annotations中的描述c.typeCheck进行操作。

答案 1 :(得分:0)

正如Eugene在他的回答中指出的那样,可以匹配整个宏应用程序的树。与每个Scala方法一样,注释宏应用程序可以采用多个类型参数列表以及多个值参数列表。

考虑名为test的注释宏的宏应用程序:

    @test[A, B][C, D](a, b)(c, d) trait Foo

test的实现中,我们可以通过

检查宏应用程序
    println(show(c.macroApplication))

将导致:

    new test[A, B][C, D](a, b)(c, d).macroTransform(abstract trait Foo extends scala.AnyRef)

要从树中提取(类型/值)参数,您必须在树上进行模式匹配。可以找到任意数量的参数列表的解析器in this project

使用此解析器检索宏应用程序的第一个值参数就像

一样简单
    val List(List(arg)) = MacroApp(c.macroApplication).termArgs