Преобразовать байт Go [] в символ C *

У меня есть байт. Буфер, который я упаковываю с данными, используя функцию binary.Write(). Затем мне нужно отправить этот байтовый массив в функцию C. Используя Go 1.6, я не смог понять это.

buf := new(bytes.Buffer) //create my buffer
....
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array
rc := C.the_function(addr, C.int(buf.Len())) //Fails here

Ошибка в строке, вызывающей функцию C, говорящую:

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

Функция C:

int the_function(const void *data, int nbytes);

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

addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0]))

Опять же, это должно работать в Go 1.6, который ввел более строгие правила указателя cgo.

Спасибо.

2 ответа

Решение

Если вы хотите использовать свой первый подход, вам нужно создать срез вне аргументов вызова функции и избегать временно выделенного заголовка среза или внешней структуры в аргументах, поэтому cgo проверки не видят его как указатель, хранящийся в Go.

b := buf.Bytes()
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len()))

C.CString метод будет более безопасным, так как данные копируются в буфер C, поэтому указатель на память Go отсутствует, и нет шанса на то, что за фрагментом bytes.Buffer будет изменен или выйдет за рамки. Вы захотите преобразовать всю строку, а не только первый байт. Этим методам нужно выделять и копировать дважды, однако, если объем данных невелик, это, вероятно, не проблема по сравнению с издержками самого вызова cgo.

str := buf.String()
p := unsafe.Pointer(C.CString(str))
defer C.free(p)
rc = C.the_function(p, C.int(len(str)))

Если 2 копии данных неприемлемы в этом решении, существует третий вариант, когда вы самостоятельно размещаете буфер C и делаете одну копию в этом буфере:

p := C.malloc(C.size_t(len(b)))
defer C.free(p)

// copy the data into the buffer, by converting it to a Go array
cBuf := (*[1 << 30]byte)(p)
copy(cBuf[:], b)
rc = C.the_function(p, C.int(buf.Len()))

Но с обоими последними вариантами не забудьте освободить указатель malloc.

Ваша программа дает сбой, потому что правила передачи указателей в C изменились в go1.6 (подробности см. На https://tip.golang.org/doc/go1.6#cgo).

Я не знаю, почему ваша программа дает сбой, поэтому я создал Go выпуск https://github.com/golang/go/issues/14546.

Но независимо от ответа по этому вопросу, я не буду использовать внутренние биты bytes.Buffer (как вы делаете), чтобы перейти в cgo напрямую. bytes.Buffer реализация может измениться в будущем, и ваша программа начнет загадочно ломаться. Я просто скопировал бы нужные вам данные в любую подходящую структуру и использовал бы их для передачи в cgo.

Другие вопросы по тегам