注:以下に編集があります! 注:下に別の編集があります!
私は、クラスに渡されてケース オブジェクトを作成する (またはむしろ入力する) Scala 注釈マクロを作成しました。ケース オブジェクトの名前は、渡されたクラスの名前と同じです。さらに重要なことは、渡されたクラスのすべてのフィールドに対して、同じ名前のケース オブジェクトにフィールドが存在することです。ただし、ケース オブジェクトのフィールドはすべて typeString
であり、それらの値は、渡されたクラスのそれぞれのフィールドの型の名前です。例:
// Using the annotation macro to populate a case object called `String`
@RegisterClass(classOf[String]) case object String
// The class `String` defines a field called `value` of type `char[]`.
// The case object also has a field `value`, containing `"char[]"`.
println(String.value) // Prints `"char[]"` to the console
ただし、これは、などの事前定義されたクラスでのみ機能するようString
です。a を定義してcase class A(...)
を実行しようとすると@RegisterClass(classOf[A]) case object A
、次のエラーが発生します。
[info] scala.tools.reflect.ToolBoxError: reflective compilation has failed:
[info]
[info] not found: type A
私は何を間違えましたか?私のマクロのコードは以下にあります。また、一般的な Scala の慣用句や悪い慣習に誰かが気付いた場合、私はヒントを気にしません。事前にどうもありがとうございました!
class RegisterClass[T](clazz: Class[T]) extends StaticAnnotation {
def macroTransform(annottees: Any*) =
macro RegisterClass.expandImpl[T]
}
object RegisterClass {
def expandImpl[T](c: blackbox.Context)(annottees: c.Expr[Any]*) = {
import c.universe._
val clazz: Class[T] = c.prefix.tree match {
case q"new RegisterClass($clazz)" => c.eval[Class[T]](c.Expr(clazz))
case _ => c.abort(c.enclosingPosition, "RegisterClass: Annotation expects a Class[T] instance as argument.")
}
annottees.map(_.tree) match {
case List(q"case object $caseObjectName") =>
if (caseObjectName.toString != clazz.getSimpleName)
c.abort(c.enclosingPosition, "RegisterClass: Annotated case object and class T of passed Class[T] instance" +
"must have the same name.")
val clazzFields = clazz.getDeclaredFields.map(field => field.getName -> field.getType.getSimpleName).toList
val caseObjectFields = clazzFields.map(field => {
val fieldName: TermName = field._1
val fieldType: String = field._2
q"val $fieldName = $fieldType"
})
c.Expr[Any](q"case object $caseObjectName { ..$caseObjectFields }")
case _ => c.abort(c.enclosingPosition, "RegisterClass: Annotation must be applied to a case object definition.")
}
}
}
編集: Eugene Burmako が指摘したようにclass A
、まだコンパイルされていないためにエラーが発生するため、java.lang.Class
for it が存在しません。私は今、これを機能させる方法を考えているすべての人のために、100 StackOverflow ポイントの報奨金を開始しました!
編集 2: ユースケースの背景: 学士論文の一部として、イベント処理システムのクエリを表現するための Scala DSL に取り組んでいます。これらのクエリは伝統的に文字列として表現されているため、多くの問題が発生します。典型的なクエリは次のようになります: 「select A.id, B.timestamp from pattern[A -> B]」。意味: のイベントがA
発生し、その後B
も同様のイベントが発生した場合はid
、そのイベントの とそのイベントA
のを教えてください。型と通常は、私が制御できない単純な Java クラスです。およびそれらのクラスのフィールドです。DSL のクエリを次のようにしたいと思います。これは、イベント タイプを表すすべてのクラスについて、たとえば、timestamp
B
A
B
id
timestamp
select (A.id, B.timestamp) { /* ... * / }
A
、理想的には同じ名前のコンパニオン オブジェクトが必要です。select
このコンパニオン オブジェクトは、次のようにフィールドを関数に渡すことができるように、それぞれのクラスと同じフィールドを持つ必要がありますselect (A.id, B.timestamp) { /* ... * / }
。A.idd
このように、関数に渡そうとするselect
と、元のクラスにそのようなフィールドがないとコンパイル時に失敗します。これは、コンパニオン オブジェクトにもフィールドがないためです。