我想使用宏来生成实例化对象的代码,如下所示:
import scala.reflect.runtime.universe._
case class Example[T: TypeTag] {
val tpe = implicitly[TypeTag[T]].tpe
}
显然,这可以转化为如下内容:
import scala.reflect.runtime.universe._
case class Example[T](implicit ev: TypeTag[T]) {
val tpe = ev.tpe
}
然后,如果这个类是用常规代码实例化的,Scala 编译器会TypeTag
自动提供实例。
但是,我想生成代码,用不同T
的 s 实例化这个类的几个实例,其中具体T
的 s 取决于用户输入,比如
sealed trait Test
case class SubTest1 extends Test
case class SubTest2 extends Test
val examples = generate[Test]
// I want this ^^^^^^^^^^^^^^ to expand into this:
val examples = Seq(Example[SubTest1], Example[SubTest2])
我知道如何获取密封特征的子类,因此我可以访问c.WeakTypeTag[SubTest1]
宏c.WeakTypeTag[SubTest2]
代码。但我不知道如何TypeTag
通过方法将它们变成预期的Example.apply
。我想过使用in()
似乎允许TypeTag
在宇宙之间传输 s 的方法,但它需要目标镜像,而且我不知道如何在编译时从宏内部获取运行时镜像。
这是我到目前为止的代码(为了更清晰,我添加了几个注释和额外的语句):
object ExampleMacro {
def collect[T] = macro collect_impl
def collect_impl[T: c.WeakTypeTag](c: Context): c.Expr[Seq[Example[_]]] = {
import c.universe._
val symbol = weakTypeOf[T].typeSymbol
if (symbol.isClass && symbol.asClass.isTrait && symbol.asClass.isSealed) {
val children = symbol.asClass.knownDirectSubclasses.toList
if (!children.forall(c => c.isClass && c.asClass.isCaseClass)) {
c.abort(c.enclosingPosition, "All children of sealed trait must be case classes")
}
val args: List[c.Tree] = children.map { ch: Symbol =>
val childTpe = c.WeakTypeTag(ch.typeSignature) // or c.TypeTag(ch.typeSignature)
val runtimeChildTpe: c.Expr[scala.reflect.runtime.universe.TypeTag[_]] = ??? // What should go here?
Apply(Select(reify(Example).tree, newTermName("apply")), runtimeChildTpe.tree)
}
Apply(Select(reify(Seq).tree, newTermName("apply")), args)
} else {
c.abort(c.enclosingPosition, "Can only construct sequence from sealed trait")
}
}
}