2013-06-22 51 views
0

說我有一個性狀Foo我具有初始值i到對象的成員宏接入其中宏被定義

val foo = new Foo(6) // class Foo(i: Int) 

後來我所說的secondMethod,在又調用實例化myMacro

foo.secondMethod(7) // def secondMethod(j: Int) = macro myMacro 

那麼,myMacro怎麼能找到i(6)的初始值?

我沒有使用c.prefixc.eval(...)等正常編譯反射成功,而是找到了2項目解決方案:

項目B:

object CompilationB { 
    def resultB(x: Int, y: Int) = macro resultB_impl 
    def resultB_impl(c: Context)(x: c.Expr[Int], y: c.Expr[Int]) = 
     c.universe.reify(x.splice * y.splice) 
} 

項目A(取決於項目B):

trait Foo { 
    val i: Int 

    // Pass through `i` to compilation B: 
    def apply(y: Int) = CompilationB.resultB(i, y) 
} 

object CompilationA { 
    def makeFoo(x: Int): Foo = macro makeFoo_impl 
    def makeFoo_impl(c: Context)(x: c.Expr[Int]): c.Expr[Foo] = 
    c.universe.reify(new Foo {val i = x.splice}) 
} 

我們可以創建一個Foo並設置i值或者與正常實例化或像makeFoo這樣的宏。第二種方法允許我們在第一次編譯時在編譯時自定義Foo,然後在第二次編譯中進一步自定義對輸入的響應(本例中爲i)!在某種程度上,我們得到了「meta-meta」功能(或「pataphysic」-capabilities ;-)

通常我們需要在範圍內有foo來反思i(例如c.eval(...)) 。但是,通過保存Foo對象內部i值,我們就可以隨時訪問,我們可以實例Foo任何地方:

object Test extends App { 
    import CompilationA._ 

    // Normal instantiation 
    val foo1 = new Foo {val i = 7} 
    val r1 = foo1(6) 

    // Macro instantiation 
    val foo2 = makeFoo(7) 
    val r2 = foo2(6) 

    // "Curried" invocation 
    val r3 = makeFoo(6)(7) 

    println(s"Result 1 2 3: $r1 $r2 $r3") 
    assert((r1, r2, r3) ==(42, 42, 42)) 
} 

我的問題

我能找到i在我的例子宏沒有這雙編譯兩輪牛車?

回答

1

事實證明,在宏內部很容易訪問Foo的成員,而不必求助於雙重編譯。下面是我們的示例宏如何訪問的i值:

val i = c.Expr[Int](Select(c.prefix.tree, TermName("i"))) 
    reify(i.splice * j.splice) 

準確地說,i這裏實際上是一個Expr,我們可以拼接,並與裏面的具體化數值工作。

所以,是有可能的宏(process)到單個編譯內,其中宏定義的對象(Foo)(Foo)的訪問權限的成員(i)(內由「定義的」我的意思是其中I使用在macro關鍵字):

object compilation { 
    def makeFoo(x: Int): Foo = macro makeFoo_impl 
    def makeFoo_impl(c: Context)(x: c.Expr[Int]): c.Expr[Foo] = 
    c.universe.reify(new Foo {val i = x.splice}) 

    def process(c: Context)(j: c.Expr[Int]): c.Expr[Int] = { 
    import c.universe._ 
    // Foo.i accessed inside macro 
    val i = c.Expr[Int](Select(c.prefix.tree, TermName("i"))) 
    reify(i.splice * j.splice) 
    } 
} 

trait Foo { 
    val i: Int 
    def apply(j: Int) = macro compilation.process 
} 

object Test extends App { 
    import compilation._ 

    val foo1 = new Foo {val i = 6} 
    Console println foo1(7) // 42 

    val foo2 = makeFoo(6) 
    Console println foo2(7) // 42 

    Console println makeFoo(6)(7) // 42 
} 

我欠這個解決方案,我發現here斯卡拉用戶列表上的提問/回答弗朗切斯科Bellomi /尤金Burmako。

作爲一個側面說明,我們並不一定需要使用c.eval(...)得到實際值一些非類型化的Expr [正確的說法是這樣的?] Tree。在大多數情況下,我們應該很好地獲得值包裝在Expr中,因爲我們可以使用該作爲將值拼接到一個具體值中,然後在那裏使用(splice-)值進行所有計算!

+0

另一個前綴爲http://stackoverflow.com/a/17028600/1296806的示例,請參閱此答案的結尾以進行自我回答:http://stackoverflow.com/a/12486993/1296806 –

+0

好的,謝謝。只是標記了我自己的答案作爲答案:-) –

+0

我其實也研究了你提到的其他答案。但是,我並沒有從這個例子中的樹解構建構中心理上跳躍到使用前綴作爲樹構建的基礎。那麼我嘗試了幾種組合,但不是'Select(c.prefix.tree,TermName(「i」))''。 –