问题 无法在Macro生成的类中访问方法


我有以下宏定义一个类并返回该类的实例(使用Scala 2.10.2和宏插件):

def test[T] = macro testImpl[T]

def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
  import c.universe._
  val className = newTypeName("Test")

  c.Expr { q"""
    class $className  {
      def method = 1
    }
    new $className
  """}
}

当我调用宏时:

case class Cat(name: String)

val t = test[Cat].method

我收到以下错误:

method method in class Test cannot be accessed in Test
val t = test[Cat].method
                   ^

我的总体目标是使用 吸血鬼的方法 并使用准引号来描述生成的类。我该如何解决这个错误?


12091
2017-08-28 06:21


起源

您的合成类不能扩展定义的特征 method 抽象? - Miles Sabin
不,因为我需要使用特定名称生成这些方法。在尝试这样做时,我意识到我甚至无法访问非生成的方法。我怀疑我在这里没有正确使用准引号。 - Eric
我不知道现在有一个宏天堂的编译器插件(而不是被迫使用分叉编译器)。如果只是为了学习那些,非常感谢你的问题。 - Régis Jean-Gilles
@Eric,请注意我加了 scala-macros 标签,使这更容易找到? - Travis Brown
当然,我真的不知道使用哪个标签。 scala-macros对我来说似乎有点多余 - Eric


答案:


在我关于吸血鬼方法的帖子中,我提到了 这个解决方法 对于 这个bug。出于某种原因,您当前无法在宏返回的实例上看到匿名类的方法,除非您创建一个使用方法扩展类的包装类并返回该实例。

你从一个稍微不同的角度看到同样的错误。您已使用要在返回的实例的结构类型上看到的方法命名该类,但您仍需要一个包装器。以下将有效:

  c.Expr { q"""
    class $className  {
      def method = 1
    }
    new $className {}
  """}

请注意,我所做的只是在创建实例的行中添加一对括号,以便我获得一个匿名类的实例 $className 而不只是一个 $className

我不知道这个bug背后是什么,我不确定Eugene是否知道更多。我最近确认它仍然在2.11的最新版本中。


10
2017-08-28 10:10



我将在下次反思会议上提出这个问题。 - Eugene Burmako
我知道你会介入特拉维斯。谢谢!现在我可以准备下一个问题:-) - Eric
看起来像是设计的。根据马丁的说法,允许当地班级的直接成员以结构类型结束导致当时出现一些奇怪的问题,这就是为什么现在必须加倍努力才能揭露这些成员。 - Eugene Burmako
行为 { class X { def foo = 42 }; new X } 虽然真的很令人惊讶...... - Eugene Burmako
github.com/scala/scala/pull/3236 - Eugene Burmako