【问题标题】:Instantiate class symbol using macro使用宏实例化类符号
【发布时间】:2015-03-30 17:35:13
【问题描述】:

我正在使用以下 Scala 宏(很大程度上受到来自 this SO question 的代码的启发)来获取给定包中包含的所有继承特定特征的对象的列表:

object Macros {
  def allObjects[T <: AnyRef](packageName: String): List[Any] = macro allObjectsImpl[T]

  def allObjectsImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
    import c.universe._

    val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
    val pkg = packageName.tree match {
      case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
    }

    val types = pkg.typeSignature.members.collect {
      case moduleSymbol: ModuleSymbol if moduleSymbol.moduleClass.asClass.baseClasses contains baseTraitSymbol => Ident(moduleSymbol)
    }.toList

    val listApply = Select(reify(List).tree, TermName("apply"))
    c.Expr[List[T]](Apply(listApply, types))
  }
}   

效果很好。

我想更改宏,以便获取所有具体的,而不是获取包中的所有对象,并提供包含以下实例的列表他们每个人。

创建对象实例时的 AST 如下所示:

scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}

scala> u showRaw ( u reify {new Object} )
res42: String = Expr(Apply(Select(New(Ident(java.lang.Object)), termNames.CONSTRUCTOR), List()))

所以我认为将我的代码更改为这样会起作用:

object Macros {
  def allInstances[T <: AnyRef](packageName: String): List[Any] = macro allInstancesImpl[T]

  def allInstancesImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
    import c.universe._

    val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
    val pkg = packageName.tree match {
      case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
    }

    def isConcreteChildClass(child: ClassSymbol, base: Symbol) = {
      !child.isAbstract && (child.baseClasses contains base)
    }

    val types = pkg.typeSignature.members.collect {
      case classSymbol: ClassSymbol if isConcreteChildClass(classSymbol, baseTraitSymbol) => {
        Apply(Select(New(Ident(classSymbol.primaryConstructor)), termNames.CONSTRUCTOR), List())
      }
    }.toList

    val listApply = Select(reify(List).tree, TermName("apply"))
    c.Expr[List[T]](Apply(listApply, types))
  }
}  

但是,当我尝试在测试包上使用更新后的宏代码时,我收到以下错误:

scala> Macros.allInstances[AnyRef]("test")
<console>:9: error: class type required but ()test.TestClass found
              Macros.allInstances[AnyRef]("test")

从我所看到的,看起来宏实际上是在返回构造函数本身,而不是返回应该由构造函数构建的实例,但我无法弄清楚我缺少什么。

【问题讨论】:

    标签: scala scala-macros


    【解决方案1】:

    问题出在这一行(为清晰起见重新格式化):

    Apply(
      Select(New(Ident(classSymbol.primaryConstructor)), termNames.CONSTRUCTOR),
      List()
    )
    

    您实际上是在选择构造函数两次。你可以直接删除primaryConstructor:

    Apply(
      Select(New(Ident(classSymbol)), termNames.CONSTRUCTOR),
      List()
    )
    

    使用ApplyConstructor 也可以:

    ApplyConstructor(Ident(classSymbol), Nil)
    

    或者你可以只使用准引号:

    q"new ${Ident(classSymbol)}()"
    

    准报价解决方案是最具前瞻性的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-29
      相关资源
      最近更新 更多