2014-02-22 87 views
3

我有下一個代碼,我想從值中提取默認參數。Scala宏:獲取參數默認值

// 
def extractor[T] = macro extractorImpl[T] 

def extractorImpl[T: c.WeakTypeTag](c: Context) = { 
    //first i got a type contructor 
    ??? 
} 

我嘗試用attachmentsattachments.all用(例如)SymbolSourceAttachment(val name: String = "new name")

SymbolSourceAttachment包含ValDef返回Set[Any],但我不知道如何從SymbolSourceAttachmentValDef提取。

順便說我應該得到一個Map[String, String]("name" -> "new name")

例子:

case class Person(name: String = "new name") 

object Macro { 
    def extractor[T] = macro extractorImpl[T] 

    def extractorImpl[T: c.WeakTypeTag](c: Context) = { 
    import c.universe._ 

    c.weakTypeOf[T].declarations.collect { 
     case a: MethodSymbol if a.isConstructor => 
     a.paramss.collect { 
      case b => b.collect { 
      case c => 
       c.attachments.all { 
       case d => println(showRaw(d)) // => SymbolSourceAttachment(val name: String = "new name") 
       } 
      } 
     } 
     } 
    } 
} 

而且宏應該返回Map("name" -> "new name")

+0

我不確定我瞭解宏應該達到什麼。你能詳細說明一下嗎?通過提供示例輸入和輸出? –

+0

@EugeneBurmako更新與例子 – lito

回答

7

既然你看到SymbolSourceAttachment,我假設你正在使用微距天堂(因爲它只是在天堂裏使用的內部附件),所以我會隨時使用quasiquotes :)

沒有簡單的方法可以在Scala反射API中獲取默認參數的值。最好的辦法是反向設計創建的方法名稱,以計算默認值,然後引用這些名稱。

SymbolSourceAttachment如果您的宏正在編譯案例類的同一編譯運行時展開工作,但它會在單獨編譯時斷開(附件不會保存在類文件中),並且它不起作用香草斯卡拉(因爲這個附件是天堂獨家)。

=== Macros.scala === 

import scala.reflect.macros.Context 
import scala.language.experimental.macros 

object Macros { 
    def impl[T](c: Context)(T: c.WeakTypeTag[T]): c.Expr[Map[String, Any]] = { 
    import c.universe._ 
    val classSym = T.tpe.typeSymbol 
    val moduleSym = classSym.companionSymbol 
    val apply = moduleSym.typeSignature.declaration(newTermName("apply")).asMethod 
    // can handle only default parameters from the first parameter list 
    // because subsequent parameter lists might depend on previous parameters 
    val kvps = apply.paramss.head.map(_.asTerm).zipWithIndex.flatMap{ case (p, i) => 
     if (!p.isParamWithDefault) None 
     else { 
     val getterName = newTermName("apply$default$" + (i + 1)) 
     Some(q"${p.name.toString} -> $moduleSym.$getterName") 
     } 
    } 
    c.Expr[Map[String, Any]](q"Map[String, Any](..$kvps)") 
    } 

    def extractor[T]: Map[String, Any] = macro impl[T] 
} 

=== Test.scala === 

case class C(x: Int = 2, y: String, z: Boolean = true)(t: String = "hello") 

object Test extends App { 
    println(Macros.extractor[C]) 
} 

17:10 ~/Projects/Paradise2103/sandbox/src/main/scala (2.10.3)$ scalac Macros.scala && scalac Test.scala && scala Test 
Map(x -> 2, z -> true) 
+0

真棒,謝謝。 – lito

+1

請注意,q「foo - > bar」不像現在的q「(foo,bar)」那樣衛生。 –

+0

如果您明確定義了伴隨對象,並嘗試訪問從其*內部應用$ default $ i,則不起作用。 – danarmak