使用宏实例化 class 符号
Instantiate class symbol using macro
我正在使用以下 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")
据我所见,宏似乎实际上返回了构造函数本身,而不是返回应该由构造函数构建的实例,但我不知道我遗漏了什么。
问题出在这一行(为清楚起见重新格式化):
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)}()"
quasiquote 解决方案是最有前途的。
我正在使用以下 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")
据我所见,宏似乎实际上返回了构造函数本身,而不是返回应该由构造函数构建的实例,但我不知道我遗漏了什么。
问题出在这一行(为清楚起见重新格式化):
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)}()"
quasiquote 解决方案是最有前途的。