【问题标题】:Accessing class definition by name from scala macro从 scala 宏中按名称访问类定义
【发布时间】:2016-03-31 12:40:45
【问题描述】:

我正在尝试创建一个 Scala 宏,它定义了一个 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 对象。

现在的问题是我需要从宏实现中访问 A 的定义(具体来说,我想看看构造函数的参数是什么)。

有没有办法以某种方式访问​​/创建 TypeTag[A]?有没有办法访问 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)
  }
}

【问题讨论】:

    标签: scala code-generation scala-macros scala-macro-paradise


    【解决方案1】:

    这需要大量的试验和错误,因为我没有设法找到关于各种类的大量文档,但我最终设法实现了我想做的事情。

    为了获取参数,我最终不得不将完全限定名称放在注释中,而不是原来的 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()
    

    使用原始帖子中提到的TypeOf 对我不起作用,因为我没有设法从TypeApply 获得正确的完全限定名称。这并没有真正使整个事情变得不安全,因为一切都是在编译时检查的,但它看起来确实不像标准 Scala。

    获得完全限定名称后,我可以使用宇宙的镜像 (The documentation on mirrors helped here) 获取 ClassSymbol,从中可以获取类构造函数参数:

    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)
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-08-23
      • 2016-10-26
      • 1970-01-01
      • 2019-09-28
      • 2021-07-17
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多