如何创建一个宏来在案例 class 中创建 val 列表?

How to create a Macro to create List of val in a case class?

我正在尝试创建一个宏来为我提供特定情况下的 val 列表 class。

object CaseClass {

  def valList[T]: List[String] = macro implValList[T]

  def implValList[T](c: whitebox.Context): c.Expr[List[String]] = {
    import c.universe._

    val listApply = Select(reify(List).tree, TermName("apply"))

    val vals = weakTypeOf[T].decls.collect {
      case m: TermSymbol if m.isVal => q"${m.name}"
    }

    c.Expr[List[String]](Apply(listApply, vals.toList))
  }

}

如此给出

case class AClass(
   val a: String,
   val b: Int
)

我想要 CaseClass.valList[AClass] = List("a", "b")

的列表

不是宏方面的专家,所以对它持保留态度。但是我用 Intellij 测试了它。

首先,要使用 weakTypeOf,您需要将 WeakTypeTag 作为隐含在您的宏实现中,如下所示:

def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]) ...

其次,要创建文字,您可以使用此构造而不是您的准引用(我相信,它实际上什么都不做):

Literal(Constant(m.name.toString))

最后,我建议使用这个守卫而不是 isVal:

m.isCaseAccessor && m.isGetter

正确检查 case class 参数并且也是 getter(case class 参数重复,一个为 isGetter,另一个为 isParam).原因是 isVal 案例 classes 的名称令人惊讶地产生了一个以空格结尾的名称。

对我有用的最终实现如下:

object CaseClass {

  def valList[T]: List[String] = macro implValList[T]

  def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]): c.Expr[List[String]] = {
    import c.universe._

    val listApply = Select(reify(List).tree, TermName("apply"))

    val vals = weakTypeOf[T].decls.collect {
      case m: TermSymbol if m.isCaseAccessor && m.isGetter => Literal(Constant(m.name.toString))
    }

    c.Expr[List[String]](Apply(listApply, vals.toList))
  }

}

作为替代方案(因为设置宏有点麻烦 - 你不能在定义它的同一个子项目中使用宏),而且你并不经常需要它,你也许可以逃脱使用 shapeless 一行:

import shapeless._
import shapeless.ops.record.Keys

case class Foo(a: Int, b: String)

Keys[the.`LabelledGeneric[Foo]`.Repr].apply().toList.map(_.name) // List("a", "b")