简体   繁体   English

如何将参数/设置传递给Scala宏?

[英]How can parameters/settings be passed to a Scala macro?

How can parameters/settings be passed to a Scala macro? 如何将参数/设置传递给Scala宏?

These settings should not be global, but per macro invocation. 这些设置不应该是全局的,而是每次宏调用。

What I would like to have is something similar to this: 我想要的是类似的东西:

def a(param: Int) = macro internalMacro("setting 1")
def b(param: Int) = macro internalMacro("setting 2")

whereas setting 1 and setting 2 should then be constant values, accessible from within the macro, so I can make the internal behavior dependent on them. 然而, setting 1setting 2应该是常量值,可以从宏内访问,因此我可以使内部行为依赖于它们。

The parameter lists of your method and the macro definition have to line up exactly, with the macro definition method having an extra initial parameter list for the context, and every other parameter from the method definition having the same name and the same type wrapped in a c.Expr . 您的方法和宏定义的参数列表必须准确排列,宏定义方法具有上下文的额外初始参数列表,并且方法定义中的每个其他参数具有相同的名称和相同的类型包含在c.Expr (Note however that the type parameter lists can differ.) (但请注意,类型参数列表可能不同。)

This means that you can't pass information to the macro implementation as arguments in your method definition. 这意味着您无法将信息作为方法定义中的参数传递给宏实现。 You can use a static annotation to accomplish the same thing, though (this is a trick I learned from Eugene Burmako, who used it to implement structural types without reflective access, aka vampire methods ): 你可以使用静态注释来完成同样的事情(这是我从Eugene Burmako那里学到的一个技巧,他使用它来实现没有反射访问的结构类型,也就是吸血鬼方法 ):

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

class setting(value: String) extends StaticAnnotation

def internalMacro(c: Context)(param: c.Expr[Int]) = {
  import c.universe._
  val settingValue = c.macroApplication.symbol.annotations.filter(
    _.tree.tpe <:< typeOf[setting]
  ).headOption.flatMap(
    _.tree.children.tail.collectFirst {
      case Literal(Constant(s: String)) => s
    }
  ).getOrElse(
    c.abort(c.enclosingPosition, "Annotation body not provided!")
  )
  settingValue match {
    case "setting 1" => c.Expr(q"42")
    case _ => param
  }
}

And then: 然后:

scala> @setting("setting 1") def a(param: Int): Int = macro internalMacro
defined term macro a: (param: Int)Int

scala> @setting("setting 2") def b(param: Int): Int = macro internalMacro
defined term macro b: (param: Int)Int

scala> def c(param: Int): Int = macro internalMacro
defined term macro c: (param: Int)Int

scala> a(10)
res0: Int = 42

scala> b(10)
res1: Int = 10

scala> c(10)
<console>:22: error: Annotation body not provided!
              c(10)
               ^

And I didn't even examine any enclosing trees. 我甚至没有检查任何封闭的树木。 See my blog post here for an example of this approach in action. 请参阅我的博客文章了解此方法的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM