Site icon Developer's tips

String transfer from a function in GO to a code in C without memory allocation (Part 2)

We were discussing a possibility of string transfer without memory allocation in the previous article. Attempts to use this method in the real projects revealed that there are cases when this method is not applicable.

Everything is working just fine if a string object is constructed from a static string constant or by means of constants concatenation. An exception occurs during program runtime if a string is a result of other objects combination.

mystr := “test string” C.Test(unsafe.Pointer(&mystr)) // works

mystr1 := "test " + "string"
C.Test(unsafe.Pointer(&mystr1))  // works

stradd := "string"
mystr2 := "test " + stradd      // Op3
C.Test(unsafe.Pointer(&mystr2)) // does not work

Console output after program execution:

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

It’s not clear where the cgoCheckPointer0 detects a pointer to a pointer. Perhaps this will be fixed in the next versions. It is also possible that a method of outwitting the cgoCheckPointer0 (please see below) will also stop working.

Solution

So, we will try to eliminate checks as such in order to avoid exclusions via converting a pointer to an integer (uintptr):

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

Function code in C in this case is:

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

Conclusion

The program works when you start it. This indicates that the data structure, which contains str string, does not differ from what is used for storage of static string constants and their concatenation – this is still the same GoString that we discussed in Part 1.

received: test test 2

gostring2.zip

Exit mobile version