从 scala 宏中按名称访问 class 定义
Accessing class definition by name from scala macro
我正在尝试创建一个 Scala 宏,它定义了一个 Class 参数,并根据 Class 的实现修改它所附加的 class ] 作为参数提供。
//Simple class with a few arguments
class A(a: String, b: String)
//Definition of this class should be modified based on class definition of A
@parameterized(classOf[A])
class B
我已经设法创建了一个简单的宏,它能够从注释中提取参数,从而生成一个 TypeName 对象,其中包含完整 class 名称的字符串表示形式。
现在的问题是我需要从宏实现中访问 A 的定义(具体来说,我想看看构造函数参数是什么)。
有没有办法以某种方式access/create TypeTag[A]?有什么方法可以访问 class A 的 AST?
为了说明我要实现的目标,这是我目前拥有的宏定义:
object parameterizedMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
import Flag._
//Extract the parameter type which was provided as an argument (rather hacky way of getting this info)
val parameterType = c.macroApplication match {
case Apply(Select(Apply(_, List(
TypeApply(Ident(TermName("classOf")), List(Ident(TypeName(parameterType))))
)) , _), _) => parameterType
case _ =>
sys.error("Could not match @parameterized arguments. Was a class provided?")
}
//Should generate method list based on the code of parameterType
val methods = ???
val result = {
annottees.map(_.tree).toList match {
case q"object $name extends ..$parents { ..$body }" :: Nil =>
q"""
object $name extends ..$parents {
..${methods}
..$body
}
"""
case q"class $name (..$args) extends ..$parents { ..$body }" :: Nil =>
q"""
class $name (..$args) extends ..$parents {
..${methods}
..$body
}
"""
}
}
c.Expr[Any](result)
}
}
这需要大量的试验和错误,因为我没能找到很多关于各种 classes 的文档,但我最终还是设法实现了我想做的事情。
为了获取参数,我最终不得不将完全限定名称放在注释中而不是原来的 TypeOf[Name]
。
@parameterized(the.fully.qualified.Name)
然后我可以使用它
val parameterType = c.macroApplication match {
case Apply(Select(Apply(_, List(
parameterType
)) , _), _) => parameterType
case _ =>
error("Could not match @parameterized arguments. Was a class provided?")
}
val fullClassName = parameterType.toString()
使用原 post 中提到的 TypeOf
对我不起作用,因为我没有设法从 TypeApply
获得正确的完全限定名称。这并没有真正使整个事情变得不安全,因为在编译时检查了所有内容,但它看起来确实有点不像标准 Scala。
一旦我有了完全限定的名称,我就可以使用宇宙的镜像 (The documentation on mirrors helped here) 来获取一个 ClassSymbol,从中可以获取 class 构造函数参数:
val constructorArguments = {
val clazz = c.mirror.staticClass(fullClassName) //Get ClassSymbol
val clazzInfo = clazz.info //Turn ClassSymbol into Type
val constructor = clazzInfo.member(termNames.CONSTRUCTOR) //Get constructor member Symbol
val constructorMethod = constructor.asMethod //Turn into MethodSymbol
val parametersList = constructorMethod.paramLists //Finally extract list of parameters
if (parametersList.size != 1)
error("Expected only a single constructor in " + fullClassName)
val parameters = parametersList.head
for (parameter <- parameters) yield {
val term = parameter.asTerm //parameter is a term
(term.name.toString, term.typeSignature)
}
}
我正在尝试创建一个 Scala 宏,它定义了一个 Class 参数,并根据 Class 的实现修改它所附加的 class ] 作为参数提供。
//Simple class with a few arguments
class A(a: String, b: String)
//Definition of this class should be modified based on class definition of A
@parameterized(classOf[A])
class B
我已经设法创建了一个简单的宏,它能够从注释中提取参数,从而生成一个 TypeName 对象,其中包含完整 class 名称的字符串表示形式。
现在的问题是我需要从宏实现中访问 A 的定义(具体来说,我想看看构造函数参数是什么)。
有没有办法以某种方式access/create TypeTag[A]?有什么方法可以访问 class A 的 AST?
为了说明我要实现的目标,这是我目前拥有的宏定义:
object parameterizedMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
import Flag._
//Extract the parameter type which was provided as an argument (rather hacky way of getting this info)
val parameterType = c.macroApplication match {
case Apply(Select(Apply(_, List(
TypeApply(Ident(TermName("classOf")), List(Ident(TypeName(parameterType))))
)) , _), _) => parameterType
case _ =>
sys.error("Could not match @parameterized arguments. Was a class provided?")
}
//Should generate method list based on the code of parameterType
val methods = ???
val result = {
annottees.map(_.tree).toList match {
case q"object $name extends ..$parents { ..$body }" :: Nil =>
q"""
object $name extends ..$parents {
..${methods}
..$body
}
"""
case q"class $name (..$args) extends ..$parents { ..$body }" :: Nil =>
q"""
class $name (..$args) extends ..$parents {
..${methods}
..$body
}
"""
}
}
c.Expr[Any](result)
}
}
这需要大量的试验和错误,因为我没能找到很多关于各种 classes 的文档,但我最终还是设法实现了我想做的事情。
为了获取参数,我最终不得不将完全限定名称放在注释中而不是原来的 TypeOf[Name]
。
@parameterized(the.fully.qualified.Name)
然后我可以使用它
val parameterType = c.macroApplication match {
case Apply(Select(Apply(_, List(
parameterType
)) , _), _) => parameterType
case _ =>
error("Could not match @parameterized arguments. Was a class provided?")
}
val fullClassName = parameterType.toString()
使用原 post 中提到的 TypeOf
对我不起作用,因为我没有设法从 TypeApply
获得正确的完全限定名称。这并没有真正使整个事情变得不安全,因为在编译时检查了所有内容,但它看起来确实有点不像标准 Scala。
一旦我有了完全限定的名称,我就可以使用宇宙的镜像 (The documentation on mirrors helped here) 来获取一个 ClassSymbol,从中可以获取 class 构造函数参数:
val constructorArguments = {
val clazz = c.mirror.staticClass(fullClassName) //Get ClassSymbol
val clazzInfo = clazz.info //Turn ClassSymbol into Type
val constructor = clazzInfo.member(termNames.CONSTRUCTOR) //Get constructor member Symbol
val constructorMethod = constructor.asMethod //Turn into MethodSymbol
val parametersList = constructorMethod.paramLists //Finally extract list of parameters
if (parametersList.size != 1)
error("Expected only a single constructor in " + fullClassName)
val parameters = parametersList.head
for (parameter <- parameters) yield {
val term = parameter.asTerm //parameter is a term
(term.name.toString, term.typeSignature)
}
}