5

我必须在使用蛋糕模式的项目中集成一些宏。这种模式使我们能够避免大量进口以及其他优势,因此我们希望保留它。现在,我们在主干外测试的一些实验性宏遇到了问题。首先,让我们展示一个名为 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"

阅读宏代码可以猜测将宏包含在静态模块中是必要的。有没有办法部署使用系统结构的宏?

4

1 回答 1

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.TypeTag 替换为 c.AbsTypeTag;为了让这个例子在 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只是一个没有任何成员的类型参数。如果您想从宏中返回特定于蛋糕的东西,也会有问题,但是当它们出现时让我们处理它们。如果需要,请提交后续问题。

于 2012-06-28T17:47:54.433 回答