编译时不会显示通过Scala宏注入的方法
我的目标是拥有一个注释宏,将方法和变量注入到trait中。虽然这似乎为trait生成了正确的代码,但我无法让编译器认识到这些方法现在已经存在。仅供参考,在尝试访问值时,实际实现确实有效 我希望用它来清理一些代码,并更好地理解以下情况下的宏: 我遵循Scala宏文档中指向的示例项目中的代码 下面是使用Scala 2.10.4和宏天堂插件2.1.0-M5编译宏的实际代码:编译时不会显示通过Scala宏注入的方法,scala,methods,macros,annotations,inject,Scala,Methods,Macros,Annotations,Inject,我的目标是拥有一个注释宏,将方法和变量注入到trait中。虽然这似乎为trait生成了正确的代码,但我无法让编译器认识到这些方法现在已经存在。仅供参考,在尝试访问值时,实际实现确实有效 我希望用它来清理一些代码,并更好地理解以下情况下的宏: 我遵循Scala宏文档中指向的示例项目中的代码 下面是使用Scala 2.10.4和宏天堂插件2.1.0-M5编译宏的实际代码: import scala.reflect.internal.annotations.compileTimeOnly import
import scala.reflect.internal.annotations.compileTimeOnly
import scala.reflect.macros.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
/**
* Represents a macro that injects appropriate getters and setters into the
* annotated trait such that it can be treated as a dependency.
*
* @param variableName The name of the variable to provide getter/setter
* @param className The class name of the variable
*/
@compileTimeOnly("Enable macro paradise to expand macro annotations")
class Dependency(
variableName: String,
className: String
) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Dependency.impl
}
object Dependency {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
println("Starting macro")
println("Prefix: " + c.prefix)
val q"new Dependency(..$params)" = c.prefix.tree
println("Params: " + params)
val (variableName: String, className: String) = params match {
case p: List[_] =>
val stringParams = p.map(_.toString.replace("\"", ""))
(stringParams(0), stringParams(1))
case _ => c.abort(
c.enclosingPosition,
"Required arguments are (variable name: String) (class name: String)"
)
}
println("Variable name: " + variableName)
println("Class name: " + className)
def modifiedTrait(traitDecl: ClassDef) = {
val (name) = try {
val q"trait $name" = traitDecl
(name)
} catch {
case _: MatchError => c.abort(
c.enclosingPosition,
"Annotation is only supported on trait"
)
}
println("Trait name: " + name)
val actualVariableName = variableName.toLowerCase
println("Actual variable name: " + actualVariableName)
val actualVariableClass = newTypeName(className)
println("Actual class name: " + actualVariableClass)
val internalVariableName = newTermName("_" + actualVariableName)
println("Internal variable name: " + internalVariableName)
val getterName = newTermName(actualVariableName)
println("Getter name: " + getterName)
val setterName = newTermName(actualVariableName + "_=")
println("Setter name: " + setterName)
val setterVariableName = newTermName("new" + actualVariableName.capitalize)
println("Setter variable name: " + setterVariableName)
val generatedTrait = q"""
trait $name {
private var $internalVariableName: $actualVariableClass = _
def $setterName($setterVariableName: $actualVariableClass) =
$internalVariableName = $setterVariableName
def $getterName: $actualVariableClass = $internalVariableName
}
"""
println("Generated trait: " + generatedTrait)
c.Expr[Any](generatedTrait)
}
annottees.map(_.tree) match {
case (traitDecl: ClassDef) :: Nil => modifiedTrait(traitDecl)
case _ => c.abort(c.enclosingPosition, "Invalid annottee")
}
}
}
下面是我在ScalaTest中使用注释的尝试:
@Dependency("someFakeField", "Int") trait TestTrait
class A extends TestTrait
val a = new A
println("TestTrait")
classOf[TestTrait].getDeclaredMethods.foreach(println)
println("A")
classOf[A].getDeclaredMethods.foreach(println)
// This line fails
//a.someFakeField = 3
以下是运行“我的宏”的输出:
Warning:scalac: Starting macro
Warning:scalac:
Warning:scalac: Prefix: Expr[Nothing](new Dependency("someFakeField", "Int"))
Warning:scalac: Params: List("someFakeField", "Int")
Warning:scalac: Variable name: someFakeField
Warning:scalac: Class name: Int
Warning:scalac: Trait name: TestTrait
Warning:scalac: Actual variable name: somefakefield
Warning:scalac: Actual class name: Int
Warning:scalac: Internal variable name: _somefakefield
Warning:scalac: Getter name: somefakefield
Warning:scalac: Setter name: somefakefield_=
Warning:scalac: Setter variable name: newSomefakefield
Warning:scalac: Generated trait: abstract trait TestTrait extends scala.AnyRef {
def $init$() = {
()
};
private var _somefakefield: Int = _;
def somefakefield_=(newSomefakefield: Int) = _somefakefield = newSomefakefield;
def somefakefield: Int = _somefakefield
}
下面是运行反射以使用trait访问trait和类的方法的输出,注意方法的外观:
TestTrait
public abstract void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.somefakefield_=(int)
public abstract com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$$outer()
public abstract int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield()
public abstract void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield_$eq(int)
public abstract int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.somefakefield()
A
public void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.somefakefield_=(int)
public com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$A$$$outer()
public com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$$outer()
public int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield()
public void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield_$eq(int)
public int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.somefakefield()
这里有一个手写的特征确实有效:
trait IncludeInterpreter {
private var _interpreter: Interpreter = _
def interpreter: Interpreter = _interpreter
def interpreter_=(newInterpreter: Interpreter) =
_interpreter = newInterpreter
}
有没有想过我做错了什么?如果我不能用宏来做这样的事情,那么我想我需要继续用手写。仅供参考,我还尝试在一个模块中编译宏,在另一个模块中使用宏为trait生成方法,并在第三个模块中实际使用trait。不确定哪些需要分开,哪些不需要分开
当我运行宏,然后尝试从ScalaTest中没有的另一个模块访问方法时,我甚至看不到使用反射的方法,尽管宏的输出仍然显示。看起来应该可以工作。你能把你的项目发布到公共的地方吗?这个代码实际上在这个公共项目的一个分支上可用:我知道代码有点重。您可以通过运行sbt kernel/run进行测试。实际相关文件如下:1。依赖项注释的规范:2。依赖项注释:3。在独立模块中编译的特征,该模块依赖于宏模块:4。依赖于编译trait的模块的模块中trait的用法:5。多模块项目的生成设置: