运行宏从Scala中的父类生成代码

运行宏从Scala中的父类生成代码,scala,class,reflection,scala-macros,Scala,Class,Reflection,Scala Macros,我有一个宏,用来生成一些代码来动态调用方法。宏比这更复杂,但为了简单起见,我们假设它的工作原理如下 def myMacro[T]: Seq[MethodName] 因此,当被要求时 class Hello { def one(a: Int, b: UserId): String = a.toString + b.id def two(c: Option[Int]): String = "" def three(d: Seq[Int], f: Set[Int]

我有一个宏,用来生成一些代码来动态调用方法。宏比这更复杂,但为了简单起见,我们假设它的工作原理如下

def myMacro[T]: Seq[MethodName]
因此,当被要求时

class Hello {
  def one(a: Int, b: UserId): String = a.toString + b.id

  def two(c: Option[Int]): String = ""

  def three(d: Seq[Int], f: Set[Int]): String = ""
}
println(myMacro[Hello]) // Seq("one", "two", "three")
我需要这个宏为我们在Candu使用的内部框架生成代码,但我需要能够从父类调用它。因此,我想要实现的是:

trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Self] // FIXME! self is not defined here...
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id

  def two(c: Option[Int]): String = ""

  def three(d: Seq[Int], f: Set[Int]): String = ""
}
val hi = new Hello
println(hi.methods) // Seq("one", "two", "three")
由于框架中有大量的类,因此在
Hello
Superclass
之间修改api是非常广泛的。因此,我需要一种在
Hello


关于如何实现这一点有什么建议吗?

如果
myMacro
Hello
之外工作,那么它也应该在
超类
内部工作

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def myMacro[T]: Seq[String] = macro impl[T]
def impl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val methodNames = weakTypeOf[T].decls
    .filter(symb => symb.isMethod && !symb.isConstructor)
    .map(_.name.toString).toList
  val methodNamesTree = methodNames.foldRight[Tree](q"Nil")((name, names) => q"$name :: $names")
  q"..$methodNamesTree"
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))
如果由于某种原因您不能使用
Hello
的名称,您可以尝试将
超类
密封并使用
knownDirectSubclass

def myMacro1(): Seq[String] = macro impl1
def impl1(c: blackbox.Context)(): c.Tree = {
  import c.universe._
  val child = c.enclosingClass.symbol.asClass.knownDirectSubclasses.head
  q"myMacro[$child]"
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))
或者您可以用
c.internal.enclosuringowner.owner.asClass
替换不推荐的
c.enclosuringclass.symbol.asClass
(现在
enclosuringowner
val方法,
enclosuringowner.owner
trait超类


如果无法将
超类
密封,请尝试遍历所有类,并查找扩展
超类的类

def myMacro2(): Seq[Seq[String]] = macro impl2

def impl2(c: blackbox.Context)(): c.Tree = {
  import c.universe._

  def treeSymbol(tree: Tree): Symbol = c.typecheck(tree, mode = c.TYPEmode).symbol
  val enclosingClassSymbol = c.internal.enclosingOwner.owner
  def isEnclosingClass(tree: Tree): Boolean = treeSymbol(tree) == enclosingClassSymbol

  var methodss = Seq[Seq[String]]()

  val traverser = new Traverser {
    override def traverse(tree: Tree): Unit = {
      tree match {
        case q"$_ class $_[..$_] $_(...$_) extends { ..$_ } with ..$parents { $_ => ..$stats }"
          if parents.exists(isEnclosingClass(_)) =>

          val methods = stats.collect {
            case q"$_ def $tname[..$_](...$_): $_ = $_" => tname.toString
          }
          methodss :+= methods

        case _ => ()
      }
        
      super.traverse(tree)
    }
  }

  c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))

  def namesToTree[A: Liftable](names: Seq[A]): Tree =
    names.foldRight[Tree](q"Seq()")((name, names) => q"$name +: $names")

  namesToTree[Tree](methodss.map(namesToTree[String](_)))
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))

如果
myMacro
Hello
外部工作,那么它也应该在
超类内部工作

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def myMacro[T]: Seq[String] = macro impl[T]
def impl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val methodNames = weakTypeOf[T].decls
    .filter(symb => symb.isMethod && !symb.isConstructor)
    .map(_.name.toString).toList
  val methodNamesTree = methodNames.foldRight[Tree](q"Nil")((name, names) => q"$name :: $names")
  q"..$methodNamesTree"
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))
如果由于某种原因您不能使用
Hello
的名称,您可以尝试将
超类
密封并使用
knownDirectSubclass

def myMacro1(): Seq[String] = macro impl1
def impl1(c: blackbox.Context)(): c.Tree = {
  import c.universe._
  val child = c.enclosingClass.symbol.asClass.knownDirectSubclasses.head
  q"myMacro[$child]"
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))
或者您可以用
c.internal.enclosuringowner.owner.asClass
替换不推荐的
c.enclosuringclass.symbol.asClass
(现在
enclosuringowner
val方法,
enclosuringowner.owner
trait超类


如果无法将
超类
密封,请尝试遍历所有类,并查找扩展
超类的类

def myMacro2(): Seq[Seq[String]] = macro impl2

def impl2(c: blackbox.Context)(): c.Tree = {
  import c.universe._

  def treeSymbol(tree: Tree): Symbol = c.typecheck(tree, mode = c.TYPEmode).symbol
  val enclosingClassSymbol = c.internal.enclosingOwner.owner
  def isEnclosingClass(tree: Tree): Boolean = treeSymbol(tree) == enclosingClassSymbol

  var methodss = Seq[Seq[String]]()

  val traverser = new Traverser {
    override def traverse(tree: Tree): Unit = {
      tree match {
        case q"$_ class $_[..$_] $_(...$_) extends { ..$_ } with ..$parents { $_ => ..$stats }"
          if parents.exists(isEnclosingClass(_)) =>

          val methods = stats.collect {
            case q"$_ def $tname[..$_](...$_): $_ = $_" => tname.toString
          }
          methodss :+= methods

        case _ => ()
      }
        
      super.traverse(tree)
    }
  }

  c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))

  def namesToTree[A: Liftable](names: Seq[A]): Tree =
    names.foldRight[Tree](q"Seq()")((name, names) => q"$name +: $names")

  namesToTree[Tree](methodss.map(namesToTree[String](_)))
}
用法:

sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro[Hello]
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
sealed trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro1()
}

val hi = new Hello
println(hi.methods) // List("one", "two", "three")
trait Superclass {
  def aFakeMethod: String = ""

  val methods = myMacro2()
}

class Hello1 extends Superclass {
  def four = ???
  def five = ???
}

class Hello extends Superclass {
  def one(a: Int, b: UserId): String = a.toString + b.id
  def two(c: Option[Int]): String = ""
  def three(d: Seq[Int], f: Set[Int]): String = ""
}
  
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))

这和CANDU反应堆有关吗?哈哈,猜对了。不,它与Candu实验室有关:这与Candu反应堆有关吗?哈哈,猜对了。不,它与Candu实验室有关:谢谢,Dmytro。不幸的是,这两种解决方案都不能在我们的代码库中工作,因为超类是由代码库中100多个类继承的。不幸的是,我们无法将其更改为
密封的
特征,并且命名所有100个类是不切实际的。谢谢Dmytro。不幸的是,这两种解决方案都不能在我们的代码库中工作,因为超类是由代码库中100多个类继承的。不幸的是,我们无法将其更改为
密封的
特征,并且命名所有100个类是不切实际的。