Site icon Заметки разработчика

Передача строки из функции на Go в код на C без выделения памяти (часть 2)

возможно ли передать строку из Go в C без выделения памяти?В прошлой статье мы обсуждали возможность передачи строки без выделения памяти. При попытке использовать этот метод в реальных проектах выяснилось, что есть случаи, когда метод неприменим. Все работает отлично, если объект string был сконструирован из статической строковой константы или конкатенацией констант. Если же string представляет собой результат объединения других объектов, при выполнении программы произойдет исключение:

mystr := "test string"
C.Test(unsafe.Pointer(&mystr))  // работает
mystr1 := "test " + "string"
C.Test(unsafe.Pointer(&mystr1))  // работает

stradd := "string"
mystr2 := "test " + stradd      // Op3
C.Test(unsafe.Pointer(&mystr2)) // не работает

При выполнении программы на экран выводится:

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

goroutine 1 [running]:
received: test string
received: test string
panic(0x464780, 0xc04204a080)
C:/Go/src/runtime/panic.go:500 +0x1af
main._cgoCheckPointer0(0xc04204a050, 0xc04204a070, 0x1, 0x1, 0xc04204a06b)
_/c_/examples/gostring/gostring/_obj/_cgo_gotypes.go:35
+0x60
main.main()
c:/examples/gostring/gostring/gostring.go:20 +0x2ae

Не понятно, где cgoCheckPointer0 обнаруживает указатель на указатель. Возможно, это будет исправлено в следующих версиях. Возможно и другое — что метод, предложенный ниже, о том, как «обмануть» cgoCheckPointer0, перестанет работать.

Решение

Итак, для того, чтобы не допустить исключения, пытаемся избежать проверки как таковой, преобразовав указатель в целое число:

package main
/*
typedef unsigned long long ULongLong;
extern void Test1(ULongLong l);
*/
import "C"
import "unsafe"

func main() {
  v1 := "test "
  v2 := "test 2"
  str := v1 + v2

  t := uintptr(unsafe.Pointer(&str))
  C.Test1(C.ULongLong(t))
}

Код функции на C в данном случае:

#include <_cgo_export.h>
#include <string.h>
#include <stdio.h> 

typedef unsigned long long ULongLong;

void Test1(ULongLong v)
{
  GoString* gs = (GoString*)v;

  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);
}

Заключение

При запуске программа работает. Это свидетельствует о том, что структура данных, в которой хранится строка str, ничем не отличается от того, что используется для хранения статических строковых констант и их конкатенации — это все тот же GoString, о котором мы говорили в части 1 («Передача строки без выделения памяти из функции на Go в код на C (часть 1)»).

received: test test 2

gostring2.zip

Exit mobile version