3

I'm trying to develop a routine in Go that will be called by a C++ program. The Go looks like the following:

package main

import (
    "C"
    "encoding/json"
    "log"
)

type keydata struct {
    Key   string `json:"key"`
    Error string `json:"Error"`
}

func lookupKey() string {
//simplified to remove the call to web service
    body := "{\"key\": \"blahblah\", \"Error\": \"\"}"

    k := keydata{}
    err := json.Unmarshal([]byte(body), &k)
    if err != nil {
        log.Fatal(err)
    }

    return k.Key
}

//export GetKey
func GetKey() string {
    theKey := lookupKey()
    return theKey
}

func main() {}

If I substitute some hard coded value for the return k.Key statement everything works fine and the C or C++ can call the exported GetKey function. When I try to return the decoded JSON string from k.Key or even just return the string from the variable named body - I receive an error:

runtime error: cgo result has Go pointer goroutine 17 [running, locked to thread]

I'm building this as follows:

go build -buildmode=c-archive example.go

The C++ is built as follow:

g++ -pthread test.cpp example.a -o test

What am I missing to make this work without raising a panic error? I'm digging around to find an answer but have yet to resolve this.

@JimB & @Jsor, thank you so much for your responses. Returning a *C.char certainly worked. I'm left wondering though, when I return it as a Go string behind the scenes in the auto generated header file Go actually creates and passes a C struct named GoString that contains a char array named p and the length named n. As long as I pass a hard-coded string instead of k.Key it actually works and I can interrogate the auto-generated char array in C++. When I try to return k.Key, a string it throws that exception. Is it possible to cast the Go string or add some notation to the export decoration to make it work?

I can certainly return the C.CString char array and make it work - thank you! I'm just also wanting to understand why it works when returning a hard coded string and not in the example I've posted.

Thank you both for your time and explanations.

2
  • 1
    (FYI, @ mentions only work in comments) You can't use the GoString safely from C, because it contains a pointer to memory allocated in Go. The fact that using a string literal gets missed by cgocheck is beside the point. Commented May 24, 2016 at 21:55
  • @JimB , thank you for the tip and the explanation. Commented May 24, 2016 at 22:03

2 Answers 2

9

You can't return a Go string to a C function. If you want a C string, you can use the C.CString function to create one and return a *C.char

//export GetKey
func GetKey() *C.char {
    theKey := lookupKey()
    return C.CString(theKey)
}

The return value from this function must be explicitly freed in the C code.

If freeing the allocated buffer isn't convenient, its common to fill a buffer provided by the caller:

func GetKey(buff *C.char, n int) int

If you can allocate the memory but don't want to handle C strings, you can insert the buffer into a pointer and return the size.

func GetKey(buff **C.char) int
Sign up to request clarification or add additional context in comments.

1 Comment

Can you explain this further, what is n here and how do I send from Python?
3

You need to use C.CString to convert Go strings to raw pointers to C strings. Note that C Strings are not garbage collected and must be freed by you elsewhere in the program.

This will make the return type *C.char which should be visible to C as a char array. It will also be your responsibility to return the buffer length (whether your write a separate function or a C struct to do that is up to you).

1 Comment

But how do I free if I am returning a CString variable?

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.