简体   繁体   English

抽象 class 或特征上的宏是否可以应用于所有子类?

[英]Is it possible for a macro on an abstract class or trait to be applied to all subclasses?

Say I have some trait (or abstract class) Foo in a library that is commonly extended by user code and requires some method numArgs :假设我在一个库中有一些特征(或抽象类) Foo ,该库通常由用户代码扩展,并且需要一些方法numArgs

trait Foo {
  // Number of arguments to the default constructor
  def numArgs: Int
}

Now numArgs is trivial to write but it's obnoxious boiler plate that I'd like to generate.现在numArgs写起来很简单,但我想生成它是令人讨厌的样板。 I can do this with reflection, but it's ugly and not portable to alternative backends like ScalaJS, ScalaNative, or GraalVM.我可以通过反射来做到这一点,但它很丑陋,并且不能移植到 ScalaJS、ScalaNative 或 GraalVM 等替代后端。

Is it possible to write a macro annotation that I can apply only to Foo ( not requiring it on every subclass of Foo ) such that the method will be generated?是否可以编写一个只能应用于Foo的宏注释(不需要Foo的每个子类上使用它)以便生成该方法?

No. Macro annotation on abstract class (or trait) can only change ASTs of this class (trait) and its companion object.否。抽象 class(或特征)上的宏注释只能更改此 class(特征)及其伴侣 object 的 AST。 You can't change parents, children, siblings etc. It's done so that macro annotations are local in this sense.你不能改变父母、孩子、兄弟姐妹等。这样做是为了让宏注释在这个意义上是本地的。

Moreover if a trait is not sealed then its subclasses can't even be known at compile time.此外,如果一个特征不是密封的,那么它的子类甚至在编译时都不知道。

If class (trait) and subclasses are nested into some object, then you can annotate the object.如果 class(特征)和子类嵌套到一些 object 中,那么您可以注释 object。

If you want to modify arbitrary trees probably you need compiler plugin or code generation (generation of sources) via Scalameta.如果您想修改任意树,您可能需要编译器插件或通过 Scalameta 生成代码(生成源代码)。

I guess in your use case you can replace macro annotation with def macro我想在你的用例中你可以用 def 宏替换宏注释

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

def numArgs[A]: Int = macro impl[A]

def impl[A: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val numArgs = weakTypeOf[A].typeSymbol.asClass.primaryConstructor.asMethod.paramLists.flatten.length
  q"$numArgs"
}

class A(i: Int, s: String)

numArgs[A] // 2

or Shapeless (if subclasses are case classes)或无形(如果子类是案例类)

import shapeless.ops.hlist.Length
import shapeless.ops.nat.ToInt
import shapeless.{::, Generic, HList, HNil, Nat}

def numArgs[A] = new PartiallyApplied[A]

class PartiallyApplied[A] {
  def apply[L <: HList, N <: Nat]()(implicit gen: Generic.Aux[A, L], length: Length.Aux[L, N], toInt: ToInt[N]): Int = toInt()
}

case class A(i: Int, s: String)

numArgs[A]() // 2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM