2015-09-17 6 views
7

से पैरामीटर्स प्राप्त करना तो मेरे पास एक फ़ंक्शन (DefDef) पर एक टिप्पणी है। इस एनोटेशन में पैरामीटर हैं। हालांकि, मैं उलझन में हूं कि कन्स्ट्रक्टर से पैरामीटर कैसे प्राप्त करें।स्कैला मैक्रो एनोटेशन

प्रयोग उदाहरण:

class Foo(b: Boolean) extends StaticAnnotation { 
    def macroTransform(annottees: Any*) = macro Foo.impl 
} 

object Foo { 
    def impl(c: whitebox.Context)(annottees: c.Tree*): c.Expr[Any] = { 
    import c.universe._ 
    //how do I get value of `b` here??? 
    c.abort(c.enclosingPosition, "message") 
    } 
} 

उत्तर

7

क्या इस बारे में:

val b: Boolean = c.prefix.tree match { 
    case q"new Foo($b)" => c.eval[Boolean](c.Expr(b)) 
} 

पूर्णता के लिए के लिए यह पूर्ण स्रोत है:

class TestMacro { 
    @Foo(true) 
    def foo(): String = "" 
    foo 
} 

यहाँ टिप्पणी के लिए कोड है

import scala.reflect.macros.Context 
import scala.language.experimental.macros 
import scala.annotation.StaticAnnotation 
import scala.annotation.compileTimeOnly 
import scala.reflect.api.Trees 
import scala.reflect.runtime.universe._ 

class Foo(b: Boolean) extends StaticAnnotation { 
    def macroTransform(annottees: Any*) :Any = macro FooMacro.impl 
} 

object FooMacro { 
    def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    import c.universe._ 
    val b: Boolean = c.prefix.tree match { 
     case q"new Foo($b)" => c.eval[Boolean](c.Expr(b)) 
    } 
    c.abort(c.enclosingPosition, "message") 
    } 
} 
+0

यह समाधान दिलचस्प है लेकिन किसी कारण से यह मेरे लिए "हैकी" लगता है। इसके अलावा, मैं चाहता हूं कि '' b'' प्रकार का होना चाहिए, किसी भी प्रकार का नहीं। किसी की तरह '' @ फू ("एएए") '' कह सकता है और लगता है कि उनका कोड कन्स्ट्रक्टर के हस्ताक्षर को देखकर संकलित होगा। इसके अलावा, अगर मैं और तर्क जोड़ने का फैसला करता हूं तो यह और अधिक भ्रमित हो जाएगा। लेकिन यह समाधान काम करता है, तो धन्यवाद! –

+0

उत्तर आपकी चिंताओं को दर्शाने के लिए संपादित किया गया। –

+0

यह कोड अब संकलित नहीं करता है। जब आप बूलियन से किसी भी पैरामीटर का प्रकार बदलते हैं, तो सब कुछ ठीक है। त्रुटि संदेश यह है: '' प्रकार की नल की अभिव्यक्ति अंतर्निहित रूपांतरण के लिए अयोग्य है ' –

1

यह एक ऐसा उत्तर है जो फेडेरिको की तकनीक पर भिन्नता दिखाता है, यदि आप एक स्थिर एनोटेशन का उपयोग करना चाहते हैं जिसमें वैकल्पिक नाम तर्क हैं। उस स्थिति में, आपको मामले मिलान विवरण में संभावित आमंत्रण अभिव्यक्तियों पर विचार करने की आवश्यकता है। एक वैकल्पिक तर्क का स्पष्ट रूप से नाम दिया जा सकता है, इसे किसी नाम के बिना दिया जा सकता है, या यह मौजूद नहीं हो सकता है। इनमें से प्रत्येक c.prefix.tree में एक अलग पैटर्न के रूप में संकलित समय पर दिखाया गया है, जैसा कि नीचे दिखाया गया है।

@compileTimeOnly("Must enable the Scala macro paradise compiler plugin to expand static annotations") 
class noop(arg1: Int, arg2: Int = 0) extends StaticAnnotation { 
    def macroTransform(annottees: Any*): Any = macro AnnotationMacros.noop 
} 

class AnnotationMacros(val c: whitebox.Context) { 
    import c.universe._ 

    // an annotation that doesn't do anything: 
    def noop(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    // cases for handling optional arguments 
    val (arg1q, arg2q) = c.prefix.tree match { 
     case q"new noop($arg1, arg2 = $arg2)" => (arg1, arg2) // user gave named arg2 
     case q"new noop($arg1, $arg2)" => (arg1, arg2)   // arg2 without name 
     case q"new noop($arg1)" => (arg1, q"0")    // arg2 defaulted 
     case _ => c.abort(c.enclosingPosition, "unexpected annotation pattern!") 
    } 

    // print out the values 
    println(s"arg1= ${evalTree[Int](arg1q)} arg2= ${evalTree[Int](arg2q)}") 

    // just return the original annotee: 
    annottees.length match { 
     case 1 => c.Expr(q"{ ${annottees(0)} }") 
     case _ => c.abort(c.enclosingPosition, "Only one annottee!") 
    } 
    } 

    def evalTree[T](tree: Tree) = c.eval(c.Expr[T(c.untypecheck(tree.duplicate))) 
} 

यहाँ एक उदाहरण मंगलाचरण कि नाम arg2 है, और इसलिए यह पहली बार पैटर्न से मेल खाएगी - case q"new noop($arg1, arg2 = $arg2)" - ऊपर:

object demo { 
    // I will match this pattern: case q"new noop($arg1, arg2 = $arg2)" 
    @noop(1, arg2 = 2) 
    trait someDeclarationToAnnotate 
} 

भी ध्यान रखें कि क्योंकि जिस तरह से इन पैटर्न काम की है, तो आप के लिए है स्पष्ट रूप से मैक्रो कोड के अंदर डिफ़ॉल्ट तर्क मान की आपूर्ति करें, जो दुर्भाग्य से थोड़ा हैकी है, लेकिन अंतिम मूल्यांकन कक्षा आपके लिए उपलब्ध नहीं है।

एक प्रयोग के रूप में, मैंने वास्तव में evalTree[scope.of.class.noop](c.prefix.tree) पर कॉल करके कक्षा बनाने की कोशिश की, लेकिन स्कैला कंपाइलर एक त्रुटि फेंकता है क्योंकि यह माना जाता है कि एनोटेशन मैक्रो कोड के अंदर एनोटेशन का संदर्भ अवैध है।

संबंधित मुद्दे