我正在尝试使用宏来创建匹配语句,它匹配给定类型的所有子类。 但是我在提取案例类的字段值时遇到了麻烦。 例如:
sealed abstract class Foobar
case class Foo(x:Int,f:Foobar) extends Foobar
case class Bar(s:String, f:Foobar) extends Foobar
现在我想创建一个看起来像这样的代码,当给出Foobar时:
e1 match {
case Foo(args) => args.toString
case Bar(args) => args.toString
}
这就是我到目前为止所得到的:
def eqImpl[A: c.WeakTypeTag](c: Context)(e1: c.Expr[A], e2: c.Expr[A]): c.Expr[Boolean] = {
import c.universe._
val tpe = c.weakTypeOf[A].typeSymbol.asClass
tpe.typeSignature // SI-7046
val subclasses = tpe.knownDirectSubclasses
val cases =
subclasses.map{ clazz =>
cq"x: $clazz => x "
}
println(cases)
reify(true)
}
此代码与Foo和Bar匹配,但我无法提取右侧所需的字段。
答案 0 :(得分:1)
所以我得到它主要是工作,这是一个例子:
def eqImpl[A: c.WeakTypeTag](c: Context)(e1: c.Expr[A], e2: c.Expr[A]): c.Expr[Boolean] = {
import c.universe._
val tpe = c.weakTypeOf[A].typeSymbol.asClass
tpe.typeSignature // SI-7046 workaround
val subclasses = tpe.knownDirectSubclasses
val cases =
subclasses.map{ case clazz : ClassSymbol =>
require (clazz.isCaseClass)
val name = clazz.companionSymbol.name
val fields = clazz.typeSignature.declarations.collect {
case m: MethodSymbol if m.isCaseAccessor => m.name}
//pattern for the fields of the left and right side side
val lFields = fields.map{ m => pq"""${m+"L":TermName}"""}
val rFields = fields.map{ m => pq"""${m+"R":TermName}"""} side
//right hand side of the case statment
val eqFields =
fields.map{ m => q"""${m+"R":TermName} == ${m+"L":TermName}"""}.reduce[Tree]{
case (acc,n) => q"$acc && $n"}
cq"($name(..$lFields),$name(..$rFields)) => $eqFields "
}
val matchStmt = q"""Tuple2[$tpe,$tpe]($e1,$e2) match {
case ..$cases
case _ => false }"""
c.Expr[Boolean](matchStmt)
}
}
此代码创建匹配元组匹配的匹配语句。 如果元组的两侧都是相同案例类的实例,则比较字段。如果所有字段相等,则返回True。 我知道这不是一个特别现实的例子,但我希望它有所帮助。 对于问题的例子,这将产生:
Tuple2[Foobar,Foobar](e1,e2) match {
case (Foo(xL,fL),Foo(xR,fR) => xL == xR && fL == fR
case (Bar(sL,fL),Bar(sR,fR) => sL == sR && fL == fR
case _ => false
}