2016-05-24 2 views
2

Я пытаюсь разработать рутину в Go, которая будет вызываться программой C++. Go выглядит следующим образом:Как вернуть строку из JSON в C-call (Golang CGO)?

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() {} 

Если я заменяю некоторые трудно закодированное значение для возвращения заявления k.Key все работает отлично и C или C++ может вызвать экспортируемую функцию GETKEY. Когда я пытаюсь вернуть декодированную строку JSON из k.Key или даже просто вернуть строку из переменной с именем body - я получаю сообщение об ошибке:

Ошибка выполнения: cgo result имеет указатель Go goroutine 17 [работает, заблокирован нить]

Я строю это следующим образом:

идут строить -buildmode = C-архив example.go

C++ построен следующим образом:

г ++ -pthread test.cpp example.a -o test

Что мне не хватает, чтобы сделать эту работу без повышения панической ошибки? Я копаю, чтобы найти ответ, но еще не решил это.

@JimB & @ Joror, большое вам спасибо за ваши ответы. Возвращение * C.char, безусловно, сработало. Мне все еще интересно, когда я возвращаю его как строку Go за кулисами в автоматически сгенерированный заголовочный файл Go фактически создает и передает структуру C с именем GoString, которая содержит массив символов с именем p и длиной n. Пока я передаю строчную кодировку вместо k.Key, она действительно работает, и я могу опросить автоматически сгенерированный массив символов в C++. Когда я пытаюсь вернуть k.Key, строка выдает это исключение. Можно ли использовать строку Go или добавить некоторые примечания к украшению экспорта, чтобы заставить его работать?

Я могу, конечно, вернуть массив символов C.CString и заставить его работать - спасибо! Я просто хочу понять, почему он работает при возврате строчной кодированной строки, а не в примере, который я опубликовал.

Благодарим вас за ваше время и объяснения.

+1

(FYI, @ упоминает работать только в комментариях) Вы не можете использовать GoString безопасно от C, поскольку она содержит указатель на память, выделенную в Go. Тот факт, что использование строкового литерала пропущен cgocheck, находится рядом с точкой. – JimB

+0

@JimB, спасибо за подсказку и объяснение. –

ответ

4

Вы не можете вернуть Go string в функцию C. Если вы хотите строку C, вы можете использовать функцию C.CString, чтобы создать и вернуть *C.char

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

возвращаемое значение из этой функции должны быть явно освобожденной в коде C.

Если освободить выделенный буфер не удобно, его общие для заполнения буфера предоставленного вызывающий:

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

Если вы можете выделить память, но не хотите, чтобы обрабатывать строки C, вы можете вставьте буфер в указатель и верните размер.

func GetKey(buff **C.char) int 
3

Вам нужно использовать C.CString, чтобы преобразовать строки Go в исходные указатели на строки C. Обратите внимание: C Строки не собираются мусором и должны быть освобождены вами в другом месте программы.

Это сделает тип возврата *C.char, который должен быть видимым C как массив char. Также вы будете обязаны возвращать длину буфера (независимо от того, записываете ли вы отдельную функцию или C-структуру, которая вам подходит).

Смежные вопросы