2017-05-09 9 views
2

Lassen Sie uns sagen, dass ich ein Build-Makro erstellt haben, die wie so verwendet werden kannHaxe benutzerdefinierte Metadaten zu Makroaufruf

@:build(macros.SampleMacro.build("arg")) 
class Main {} 

Ist es möglich, sie in eine kundenspezifische, Stenografie Metadaten zu konvertieren?

@:samplemacro("arg") 
class Main {} 

Irgendwelche Unterlagen zu diesem Thema?

Antwort

2

Nach vielen Stolper Ich habe herausgefunden, dass es möglich ist.

Ein Teil der Lösung ist

--macro addGlobalMetadata('$path', '@:build(Build.build())') 

verwenden Auf diese Weise können Sie eine @ zuweisen: Build-Funktion, um alle Klassen in der $ Pfad. Dies kann entweder als Compileroption oder als Haxeflag verwendet werden.

Aber das allein genügt nicht, Metadaten-Tags zu übernehmen, die dynamische Argumente übernehmen. Aber da wir nun Build.build() haben, das für alle Klassen, ausgeführt wird, kann Build.build() - Funktion sowohl prüfen, welche Klassen unsere benutzerdefinierten Metadaten-Tags haben, als auch alles bauen für diese Klassen brauchen wir vielleicht.

für unsere eigene Metadaten-Tags Um zu überprüfen, haben wir unsere Prüfung Makro wie folgt zusammen:

class Build { 

    static var META_STR:String = ":samplemacro"; 

    macro static public function build():Array<Field> { 

     // We check only those Contexts for where a class type exists 
     var localClass:Null<Ref<ClassType>> = Context.getLocalClass(); 
     if(localClass == null) return null; // no class type 

     // We check if the metadata for the class contains our 
     // custom metadata string at all 
     if(!localClass.get().meta.has(META_STR)) return null; 

     // This class does have our custom metadata! 
     // Because there may be more than one of the same type 
     // of metadata, we extract a list of all the custom metadata tags 

     var customTags = localClass.get().meta.extract(META_STR); 

     // For each tag we can get at the arguments passed in 
     // by accessing the params field 
     for(customTag in customTags){ 
      var params = customTag.params; 

      // Here we can handle each of our @:samplemacro(params) tags, 
      // save the params for use later, or 
      // pass the arguments over to another class to deal with 
     } 

     var fields = Context.getBuildFields(); 

     // Modify the class fields the way you want 

     // Optionally destroy the metadata tags afterwards 
     // with localClass.get().meta.remove(META_STR); 

     return fields; 
    } 
} 

Weitere Details zu addGlobalMetadata zu finden unter: https://stackoverflow.com/a/38075492/1502818 Sie für Ihren Vorschlag

+1

Sie können überprüfen, wie sich dies auf die Kompilierungsleistung auswirkt. Das Ausführen eines Build-Makros für alles könnte in einem großen Projekt teuer sein (im Grunde der Grund, warum ich diesen Ansatz nicht vorgeschlagen habe). – Gama11

+1

Ich muss noch auf ein Leistungsproblem stoßen, und außerdem kann ich anscheinend keinen anderen Weg finden. Zum Glück beinhaltet der Test zur Überprüfung der Existenz eines Tags nur die Überprüfung einer Karte von Metadaten-Tags. –

+2

gibt es eine Bibliothek, die genau das tut, mit einigen zusätzlichen Funktionen: https://github.com/haxetink/tink_syntaxhub – KevinResoL

3

Ich bin mir nicht sicher, ob das möglich ist, aber Sie können die Tatsache nutzen, dass @:autoBuild() Metadaten auf Schnittstellen funktioniert. Dies wird häufig für „Marker-Schnittstellen“ wie folgt verwendet:

class Main implements ISampleMacro {} 

@:autoBuild(macros.SampleMacro.build("arg")) 
interface ISampleMacro {} 

jedoch vermutlich Sie ein anderes "arg" pro Nutzung haben wollen, anstatt es zu. Sie können dies erreichen, indem ein @:const Typ-Parameter verwenden:

class Main implements ISampleMacro<"foo"> {} 

@:autoBuild(macros.SampleMacro.build()) 
interface ISampleMacro<@:const T> {} 

die Art Parameterwert in Ihrem Build Makro Extrahieren erfordert etwas mehr Aufwand, als nur einen Parameter, um es vorbei:

switch (Context.getLocalClass().get().interfaces[0].params[0]) { 
    case TInst(_.get() => t, params): 
     switch (t.kind) { 
      case KExpr({expr: EConst(CString(arg)), pos: _}): 
       trace(arg); // "foo" 
      case _: 
     } 
    case _: 
} 
+0

danken - das ist eine andere Art, Dinge zu tun, aber nicht das, wonach ich suchte. –

Verwandte Themen