我必须在一个使用蛋糕模式的项目中集成一些宏。这种模式使我们能够避免数以万计的进口,以及其他优势,因此我们希望保留它。现在,我们正面临一些问题,我们已经在主干外测试了一些实验宏。首先,让我们展示一个名为Cake的虚拟系统:
trait APiece {
class A
}
trait BPiece { this: APiece =>
def aMacro(a: A): Unit = () /* macro ??? */
}
trait CPiece { this: APiece with BPiece =>
def aMacroInvoker = aMacro(new A)
}
class Cake { this: APiece with BPiece with CPiece => }
APiece 定义一个类, BPiece 应该是一个使用APiece定义类的宏,最后, CPiece 调用宏。我说BPiece应该是一个宏,因为我无法为它编写实现代码。我尝试了几种方法,但总是崩溃,出现以下错误:
"macro implementation must be in statically accessible object"
阅读macros code可以猜测将宏封装在静态模块中是必要的。有没有办法部署使用系统结构的宏?
答案 0 :(得分:4)
幸运的是,您可以轻松解决问题。
但首先,让我来回顾一下。在第一个原型中,宏被定义为:def macro aMacro(a: A): Unit = ...
。我们在准备SIP时所取得的重大突破之一是分离宏定义(宏的公共面)和宏实现(承载宏逻辑的树变换器)。我花了一段时间才意识到它有多酷,但现在每当我写一个宏声明时,我都会高兴得发光。
所以,回到你的问题。当然,宏实现必须是静态可访问的(否则,编译器将无法在编译期间加载和调用它们)。但是,宏定义没有此限制,因此您可以编写如下定义:
trait BPiece { this: APiece =>
def aMacro(a: A): Unit = macro Macros.aMacro
}
从定义中引用的宏实现可以放入您希望的任何对象中,甚至可以放入不同的编译单元中。
唯一缺少的难题是我们将如何从实现中引用A
,因为A
是在蛋糕内定义的。最简单的方法是使aMacro
通用并依赖于类型推断:
(更新:为了使这个例子在2.10.0-M7中工作,你需要用c.AbsTypeTag替换c.TypeTag;为了使这个例子在2.10.0-RC1中工作,c.AbsTypeTag需要替换为c.WeakTypeTag)
trait BPiece { this: APiece =>
def aMacro[A](a: A): Unit = macro Macros.aMacro[A]
}
object Macros {
def aMacro[A: c.TypeTag](c: Context)(a: c.Expr[A]): c.Expr[Unit] = c.literalUnit
}
但是,这不允许您使用reify
,因为对于宏实现,A
只是一个没有任何成员的类型参数。如果你想要从宏中返回特定于蛋糕的东西,也会出现问题,但是当它们出现时让我们处理它们。如果需要,请提交后续问题。