2

I want to call a go function from C++ and return a string.

The function:

//export EditStr
func EditStr(a string)  (ostr string) {
    ostr = fmt.Sprintf("[["+a+"]]")
    fmt.Println(">> " + ostr)
    return 
}

CGO seems happy to compile this and generates:

extern GoString EditStr(GoString a);

When calling the function from C++

auto res = EditStr(GoString{.p = "asdf", .n = 4});

My debug output works (so passing the string to go seems fine), but it fails when returning the string.

>> [[asdf]]
panic: runtime error: cgo result is unpinned Go pointer or points to unpinned Go pointer

The documentation (https://pkg.go.dev/cmd/cgo#hdr-C_references_to_Go) seems to imply GoString works for passing data into go but does not really say anything about returning it.

The example MyFunction2 seems to hint *C.char should be used to return strings, but CGO seems to be happy returning string directly.

Is this just a bug / missing warning / missing error in CGO or is there a way to return strings directly?

(I know I could use (int64, *C.char) as a return value, but it seems silly to manually return the values if there is the GoString struct seeming to be intended to package those values.)

5
  • 2
    That same documentation also says “Note that there is no way for C code to create a value of this type (_GoString_); this is only useful for passing string values from Go to C and back to Go”. You need to return a type that you can use in C. Commented Aug 24 at 18:10
  • "this is only useful for passing string values from Go to C" which does not work. Commented Aug 26 at 9:00
  • You can't just remove some of the words from the statement to make it mean what you want. It specifically says "from Go to C and back to Go", which I would interpret as a call originating from Go, and passed through C to another Go function. What you are doing is definitely not that. The fact that you can attempt the call you did does not avoid the cgo pointer rules, and the runtime is issuing the expected panic when those rules are violated. Commented Aug 26 at 10:53
  • If you continue to read te docs, you will also find "The _GoString_ type also may not be pinned with runtime.Pinner. Because it includes a Go pointer, the memory it points to is only pinned for the duration of the call; _GoString_ values may not be retained by C code.". Commented Aug 26 at 10:59
  • Well I can't pass the _GoString_ back to GO, since I get a runtime error before I am able to do this. Thus I can't even treat it as an opaque handle which I can only pass back to GO. Commented Sep 15 at 8:44

1 Answer 1

1

The panic you're seeing is telling you that you're passing a Go pointer into C (as GoString.p), which will result in undefined behavior as Go's garbage collector may collect or relocate it before it's used. The GoString type isn't meant to be used from C. As mentioned in the documentation you linked:

Go functions that take arguments of type string may be called with the C type _GoString_, described above. The _GoString_ type will be automatically defined in the preamble. Note that there is no way for C code to create a value of this type; this is only useful for passing string values from Go to C and back to Go.

The purpose of that type is to call C functions from Go with a string argument, and allow those C functions to pass it directly to other Go functions.

If you want to pass a string into C, you'll need to place it into into memory allocated by C (using malloc() & co.), which is what C.CString() does for you.

Sign up to request clarification or add additional context in comments.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.