2015-08-21 11 views
10

Soweit ich weiß (siehe here und here) gibt es keinen Typ Discovery-Mechanismus in der reflect package, die erwartet, dass Sie bereits eine Instanz des Typs oder Werts, den Sie überprüfen möchten.Go (reflect): Wie erkennt man alle Pakettypen zur Laufzeit?

Gibt es eine andere Möglichkeit, alle exportierten Typen (vor allem die Strukturen) in einem laufenden Go-Paket zu entdecken?

Hier ist, was ich wünschte, ich hätte (aber es existiert nicht):

import "time" 
import "fmt" 

func main() { 
    var types []reflect.Type 
    types = reflect.DiscoverTypes(time) 
    fmt.Println(types) 
} 

Das Endziel ist in der Lage sein, alle Strukturen eines Pakets zu entdecken, die bestimmte Kriterien erfüllen, dann in der Lage sein, um neue Instanzen dieser Strukturen zu instanziieren.

BTW, eine Registrierungsfunktion, die die Typen identifiziert, ist nicht ein gültiger Ansatz für meinen Anwendungsfall.


Ob Sie denken, es ist eine gute Idee ist oder nicht, hier ist, warum ich diese Fähigkeit wollen (weil ich weiß, Sie gehen fragen):

Ich habe geschrieben eine code generation utility, die Lasten gehen Quelldateien und erstellt eine AST zum Suchen nach Typen, die einen bestimmten Typ einbetten. Die Ausgabe des Dienstprogramms besteht aus einer Reihe von Go-Testfunktionen, die auf den erkannten Typen basieren. Ich rufe dieses Dienstprogramm mit go generate auf, um die Testfunktionen zu erstellen, und führe dann go test aus, um die generierten Testfunktionen auszuführen. Jedes Mal, wenn sich die Tests ändern (oder ein neuer Typ hinzugefügt wird), muss ich erneut go generieren ausführen, bevor go test erneut ausgeführt wird. Aus diesem Grund ist eine Registrierungsfunktion keine gültige Option. Ich möchte den Schritt go generate vermeiden, aber dazu müsste mein Dienstprogramm eine Bibliothek werden, die vom laufenden Paket importiert wird. Der Bibliothekscode müsste den laufenden Namespace während init() nach Typen durchsuchen, die den erwarteten Bibliothekstyp einbetten.

+0

Das einzige, worüber ich nachdenken kann, ist das Erzeugen von Code, der eine Registerfunktion in 'init()' für Sie aufruft. – THUNDERGROOVE

+0

Yeah @THUNDERGROOVE, das ist im Grunde, was ich implementiert habe. Aber ich möchte keinen Code generieren. Ich lasse eher 'go test' laufen und lasse die Bibliothek alle Typen entdecken, instantiieren und dann aufrufen. Alles in einem Prozess ausgeführt. Es ist ein doozy des Problems. – mdwhatcott

Antwort

1

Nein, gibt es nicht.

Wenn Sie Ihre Typen "kennen" wollen, müssen Sie sie registrieren.

2

Leider glaube ich nicht, dass das möglich ist. Pakete sind in Go nicht umsetzbar, Sie können keine Funktion aufrufen. Sie können auch keine Funktion für einen Typ aufrufen, aber Sie können reflect.TypeOf für eine Instanz des Typs aufrufen und erhalten reflect.Type, was eine Laufzeitabstraktion eines Typs ist. Es gibt einfach keinen solchen Mechanismus für Pakete, es gibt keine reflect.Package.

Mit dieser sagte, man kann file an issue über das Fehlen von (und Praktikabilität der Zugabe) reflect.PackageOf usw.

+0

Ja, ich kann immer noch ein Problem öffnen, da es sehr schön wäre, etwas wie reflect.PackageOf ("path") zu haben. Types() '. – mdwhatcott

9

Warnung: ungetestet und Hacky. Kann brechen, wenn eine neue Version von Go veröffentlicht wird.

Es ist möglich, alle Arten der Runtime weiß zu bekommen, indem man Gos Laufzeit etwas hackt.Fügen Sie eine kleine Baugruppendatei in Ihrem eigenen Paket, bestehend aus:

TEXT yourpackage·typelinks(SB), NOSPLIT, $0-0 
    JMP reflect·typelinks(SB) 

In yourpackage erklären den Funktionsprototyp (ohne Gehäuse):

func typelinks() []*typeDefDummy 

Neben einer Typdefinition:

type typeDefDummy struct { 
    _  uintptr   // padding 
    _  uint64   // padding 
    _  [3]uintptr  // padding 
    StrPtr *string   
} 

Dann rufen Sie einfach Typelinks auf, durchlaufen das Segment und lesen jedes StrPtr für den Namen. Suchen Sie diejenigen, die mit yourpackage beginnen. Beachten Sie, dass diese Methode nicht eindeutig funktioniert, wenn zwei Pakete mit dem Namen yourpackage in verschiedenen Pfaden vorhanden sind.

kann ich irgendwie in das reflect-Paket einhaken, um neue Instanzen dieser Namen zu instanziieren?

Ja, d unter der Annahme ist ein Wert vom Typ *typeDefDummy (das Sternchen beachten Sie, sehr wichtig):

t := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&d))) 

Jetzt t ist ein reflect.Type Wert, den Sie reflect.Value s instanziiert verwenden können.


Edit: Ich getestet und ausgeführt, um diesen Code erfolgreich und uploaded it as a gist haben.

Passen Sie die Paketnamen an und fügen Sie ggf. Pfade hinzu.

+1

Das ist eine nette ;-) – Volker

+1

Heilige Versammlung, Batman! –

+0

@thwd: Wow, das ist verrückt. Nun zur goldenen Frage: Kann ich nach dem Aufruf von typelinks(), um die [] String-Typ-Namen zu erhalten, irgendwie in das reflect-Paket einhaken, um neue Instanzen dieser Namen zu instanziieren? – mdwhatcott

11

In Go 1.5 können Sie das neue Paket types und importer verwenden, um Binär- und Quellpakete zu überprüfen. Zum Beispiel:

package main 

import (
    "fmt" 
    "go/importer" 
) 

func main() { 
    pkg, err := importer.Default().Import("time") 
    if err != nil { 
     fmt.Printf("error: %s\n", err.Error()) 
     return 
    } 
    for _, declName := range pkg.Scope().Names() { 
     fmt.Println(declName) 
    } 
} 

können Sie das Paket verwenden go/build installiert alle Pakete zu extrahieren. Oder Sie können den Importeur Lookup konfigurieren, um Binärdateien außerhalb der Umgebung zu überprüfen.

Vor 1.5, der einzige nicht-hacky Weg ist, das Paket ast zu verwenden, um den Quellcode zu kompilieren.

+0

Wow, wusste nicht, dass diese neue Funktionalität existiert. Was für ein gutes Timing! Nun zur goldenen Frage: Nach dem Abrufen von pkg.Scope().Names(), kann ich irgendwie in das reflect-Paket einhaken, um neue Instanzen der Names() zu instanziieren, die Typen darstellen? – mdwhatcott

+1

Scheint, dass Sie nach der Plugin-Architektur suchen. Derzeit ist es in Go 1.5 nicht implementiert. Lesen Sie https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit# für weitere Informationen. – Alvivi

+1

Mein Gehirn explodiert gerade. – mdwhatcott

Verwandte Themen