2016-05-24 8 views
2

Ich versuche, eine Routine in Go zu entwickeln, die von einem C++ - Programm aufgerufen wird. Die Go sieht wie folgt aus:Wie wird eine Zeichenfolge von JSON an einen C-Aufrufer (Golang CGO) zurückgegeben?

package main 

import (
    "C" 
    "encoding/json" 
    "log" 
) 

type keydata struct { 
    Key string `json:"key"` 
    Error string `json:"Error"` 
} 

func lookupKey() string { 
//simplified to remove the call to web service 
    body := "{\"key\": \"blahblah\", \"Error\": \"\"}" 

    k := keydata{} 
    err := json.Unmarshal([]byte(body), &k) 
    if err != nil { 
     log.Fatal(err) 
    } 

    return k.Key 
} 

//export GetKey 
func GetKey() string { 
    theKey := lookupKey() 
    return theKey 
} 

func main() {} 

Wenn ich für die Rückkehr k.Key Aussage alles etwas hart codierten Wert ersetzen funktioniert gut und die C oder C++ können die exportierte GetKey Funktion aufrufen. Wenn ich versuche, die decodierte JSON-Zeichenfolge von k.Key zurückzugeben oder nur die Zeichenfolge aus der Variablen mit dem Namen body zurückzugeben - erhalte ich einen Fehler:

Laufzeitfehler: cgo Ergebnis hat Go-Zeiger goroutine 17 [ausgeführt, gesperrt zu thread]

ich baue dies wie folgt:

-buildmode = c-Archiv example.go

Die C++ gebaut wird wie folgt bauen gehen:

g ++ -pthread test.cpp beispiel.a -o test

Was fehlt mir, damit das funktioniert, ohne einen Panikfehler zu verursachen? Ich suche nach einer Antwort, muss sie aber noch lösen.

@JimB & @Jsor, vielen Dank für Ihre Antworten. Die Rückkehr einer * C.char hat sicherlich funktioniert. Ich frage mich jedoch, wenn ich es als eine Go-Zeichenfolge hinter den Kulissen in der automatisch generierten Header-Datei Go erzeugt und übergibt eine C-Struktur mit dem Namen GoString, die ein Char-Array namens p und die Länge mit dem Namen n enthält. Solange ich eine hard-codierte Zeichenfolge anstelle von k.Key übergebe, funktioniert es tatsächlich und ich kann das automatisch erzeugte char-Array in C++ abfragen. Wenn ich versuche, k.Key zurückzugeben, wirft eine Zeichenkette diese Ausnahme. Ist es möglich, die Go-Zeichenfolge zu konvertieren oder eine Notation zur Export-Dekoration hinzuzufügen, damit sie funktioniert?

Ich kann das C.CString Char-Array sicherlich zurückgeben und es funktioniert - danke! Ich möchte nur verstehen, warum es funktioniert, wenn eine hart codierte Zeichenfolge zurückgegeben wird und nicht in dem Beispiel, das ich gepostet habe.

Vielen Dank für Ihre Zeit und Erklärungen.

+1

(FYI, @ Erwähnungen funktionieren nur in Kommentaren) Sie können den GoString nicht sicher von C aus verwenden, da er einen Zeiger auf den in Go zugewiesenen Speicher enthält. Die Tatsache, dass ein String-Literal von cgocheck nicht erkannt wird, ist nebensächlich. – JimB

+0

@ JimB, danke für den Tipp und die Erklärung. –

Antwort

4

Sie können eine Go string nicht an eine C-Funktion zurückgeben. Wenn Sie einen C-String wollen, können Sie die C.CString Funktion nutzen zu erstellen und geben einen *C.char

//export GetKey 
func GetKey() *C.char { 
    theKey := lookupKey() 
    return C.CString(theKey) 
} 

Der Rückgabewert dieser Funktion muss explizit im C-Code befreit werden.

Wenn die zugewiesenen Puffer zu befreien ist nicht bequem, sein gemeinsamer einen Puffer vom Anrufer zur Verfügung gestellt zu füllen:

func GetKey(buff *C.char, n int) int 

Wenn Sie den Speicher zuordnen können, aber nicht wollen, C-Strings zu handhaben, können Sie Fügen Sie den Puffer in einen Zeiger ein und geben Sie die Größe zurück.

3

Sie müssen C.CString verwenden, um Go-Zeichenfolgen in unformatierte Zeiger in C-Zeichenfolgen zu konvertieren. Beachten Sie, dass C-Strings nicht als Müll gesammelt werden und von Ihnen an anderer Stelle im Programm freigegeben werden müssen.

Dies wird den Rückgabetyp *C.char machen, der für C als char Array sichtbar sein sollte. Es liegt auch in Ihrer Verantwortung, die Pufferlänge zurückzugeben (ob Sie eine separate Funktion oder eine C-Struktur schreiben, um das zu tun, hängt von Ihnen ab).