如何在Scala宏中创建类或对象?

6

我尝试使用宏从一个样例类中生成伴生对象,但是我很难找到任何如何完成这个任务的示例。

例如:

case class Person(name: String, age: Int, id: Option[Int] = None)

如果我执行以下操作:
object PersonTable extends TypedTable[Person]

我希望它能够生成以下内容:
object PersonTable extends Table("PERSON") {
  val name = column[String]("NAME")
  val age = column[Int]("AGE")
  val id = column[Option[Int]]("ID")
}

此外,我希望能够扩展它并添加其他字段:

object PersonTable extends TypedTable[Person] {
  val created = column[Timestamp]("TIMESTAMP")
}

它会生成:

object PersonTable extends Table("PERSON") {
  val name = column[String]("NAME")
  val age = column[Int]("AGE")
  val id = column[Option[Int]]("ID")
  val created = column[Timestamp]("TIMESTAMP")
}

编辑

在阅读了关于宏注解的内容后(感谢 Eugene 和 Mario),我创建了以下代码:

class table[T] extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro TableGenerator.impl
}

object TableGenerator {
  def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    def modifiedObject(objectDef: ModuleDef): c.Expr[Any] = {
      val ModuleDef(_, objectName, template) = objectDef
      val ret = q"""
object $objectName {
  def test() = println("Wahoo!")
}
      """
      c.Expr[Any](ret)
    }

    annottees.map(_.tree) match {
      case (objectDecl: ModuleDef) :: _ => modifiedObject(objectDecl)
      case x => c.abort(c.enclosingPosition, s"@table can only be applied to an object, not to $x")
    }
  }
}

然后尝试像这样使用它:

@table[String] object MyCoolObject
MyCoolObject.test()

第一行可以正常运行,但第二行报错找不到测试方法。我该如何使测试方法可见?

2
可以通过宏注释来实现这一点:如果您给类添加注释,宏将在其参数中接收到类和对象的AST。在宏中,您将能够调整并发射两者。 - Eugene Burmako
正如@EugeneBurmako所说,宏注解似乎是前进的道路。您可能想看一下https://github.com/yetu/scala-beanutils以获取一些灵感。 - Mario Camou
感谢@EugeneBurmako和Mario。请根据您的建议查看我的编辑。 - darkfrog
我仍然希望在这里得到回应。我在创建的对象上失去了类型信息,感到困惑。 - darkfrog
你能把整个项目发布到GitHub上吗?看起来原理上应该是可以运行的。 - Eugene Burmako
事实证明它是有效的,只是我的构建脚本中有一个错误。我希望Scala编译器能够给出更好的解释,以帮助指导我如何修复它。 - darkfrog
1个回答

8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接