2016-06-25 2 views
2

Я пытаюсь взаимодействовать с некоторым кодом C от Go. Используя cgo, это было относительно прямолинейно, пока я не попал в этот (довольно распространенный) случай: нужно передать указатель на структуру, которая сама содержит указатель на некоторые данные. Я не могу понять, как это сделать от Go, не прибегая к созданию структуры в сам код C, который я бы предпочел не делать. Вот отрывок, который иллюстрирует эту проблему:Как я могу заполнить указатель void * C в Go?

package main 

// typedef struct { 
//  int size; 
//  void *data; 
// } info; 
// 
// void test(info *infoPtr) { 
//  // Do something here... 
// } 
import "C" 

import "unsafe" 

func main() { 
    var data uint8 = 5 
    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: unsafe.Pointer(&data)} 
    C.test(info) 
} 

Хотя это нормально компилируется, пытаясь запустить его результаты в:

panic: runtime error: cgo argument has Go pointer to Go pointer 

В моем случае, данные передается на вызов C не (т. е. рассматриваемый код C врывается в структуру, копирует то, что ему нужно, а затем возвращает).

ответ

2

См "Passing pointers" раздел cgo документации:

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

А также:

Эти правила проверяются динамически во время выполнения. Проверка контролируется установкой cgocheck переменной окружения GODEBUG. По умолчанию используется GODEBUG = cgocheck = 1, который реализует разумно дешевые динамические проверки. Эти проверки могут быть полностью отключены с помощью GODEBUG = cgocheck = 0. Полная проверка обработки указателя, за небольшую плату во время выполнения, доступна через GODEBUG = cgocheck = 2.

Если запустить фрагмент кода вы предоставили с:

GODEBUG=cgocheck=0 go run snippet.go 

Тогда нет никакой паники. Тем не менее, правильный путь заключается в использовании C.malloc (или получить «C указатель» откуда-то еще):

package main 

// #include <stdlib.h> 
// typedef struct { 
//  int size; 
//  void *data; 
// } info; 
// 
// void test(info *infoPtr) { 
//  // Do something here... 
// } 
import "C" 

import "unsafe" 

func main() { 
    var data uint8 = 5 

    cdata := C.malloc(C.size_t(unsafe.Sizeof(data))) 
    *(*C.char)(cdata) = C.char(data) 
    defer C.free(cdata) 

    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: cdata} 
    C.test(info) 
} 

Это работает, потому что в то время как обычные указатели Go не разрешены, C.malloc возвращает «указатель C»:

указателя Перейти означает указатель на области памяти, выделенной Go (например, с помощью оператора & или вызова предопределенной новой функции) и указателя на член с означает указатель на области памяти, выделенной C (например, при вызове к C.malloc). Является ли указатель указателем Go или C-указателем, является динамическим свойством, определяемым тем, как была распределена память.

Обратите внимание, что вам необходимо включить stdlib.h, чтобы использовать C.free.

+1

Спасибо. Вот что я в итоге ... Я просто надеялся на что-то немного менее подробное. –