Если посмотреть, какой код генерируется для экспортируемых для доступа из С функций GO c параметром типа string,
//export MyGoCallback func MyGoCallback(param1 string) { … }
то можно увидеть, что тип param1 в сгенерированной функции — это GoString. Это структура, которая объявляется в генерируемом при построении включаемом файле _cgo_export.h как
typedef struct { const char *p; GoInt n; } GoString;
Если string — это структура GoString, почему бы просто не передавать ее адрес в C, а не выделять память, копировать строку в выделенный буфер, освобождать память после использования?
Прилагаемый пример реализует схему с передачей строки без выделения памяти. При этом в функцию на C передается адрес объекта string как unsafe.Pointer и далее, после приведения типа к указателю на GoString, получаем прямой доступ к данным строки. При этом нужно учесть, что строка в кодировке utf8 и не завершена нулем.
Реализация
Код модуля на GO (gostring.go):
package main // extern void Test(void* p); import "C" import "unsafe" func main() { str := "test test" C.Test(unsafe.Pointer(&str)) }
Код функции Test() на C (test.c):
#include <_cgo_export.h> #include <string.h> #include <stdio.h> void Test(void* p) { GoString* gs = (GoString*)p; char buf[64]; int n = gs->n < sizeof(buf) - 1 ? gs->n : sizeof(buf) - 1; strncpy(buf, gs->p, n); buf[n] = '\0'; printf("received: %s\n", buf); }
В результате построения примера командой
go build .
и запуска приложения видим на экране:
received: test test
Функция Test обрезает строку, если ее длина превышает 63 символа. В реальной ситуации нужно использовать буфер большего размера и использовать динамически выделенный буфер, если этого размера оказывается недостаточно. В прилагаемых файлах gostr2c.c и gostr2c.h можно найти полную реализацию функций для использования пришедшего из кода на GO указателя на объект string. С использованием макросов из gostr2c.h функцию Test можно переписать как:
void Test(void* p) { GO2C(p); printf("received: %s\n", pp); GO2C_FREE(p); }
Заключение
Почему же этот способ не документирован в GO? Всегда ли предложенная схема будет работать? Читайте об этом во второй части…