1

I'm trying to call a C function from Go with cgo to read an error message. The function produces a message of an unknown length less than 256 bytes.

Working example in C:

char message[ERROR_SIZE]; //256
last_error( message, sizeof(message) );         
printf( "message: %s\n", message );

My attempt in Go (not working):

var ptr *C.char
C.last_error(ptr, ERROR_SIZE)
var message = C.GoString(ptr)
fmt.Printf("message: %s\n", message)

When the go code is run, the message is empty. Does the go version need to preallocate space for the message? How to do this?


Update after comment by LPs to pass an array. This works, but seems a bit awkward:

var buf [ERROR_SIZE]byte
var ptr = (*C.char)(unsafe.Pointer(&buf[0]))
C.last_error(ptr, len(buf))
var message = C.GoString(ptr)
fmt.Printf("message: %s\n", message)

Is there a simpler way?

6
  • 1
    Not a go expert, but you c function takes an array. From go side you are passing a simple pointer. Commented Oct 24, 2016 at 7:42
  • 1
    Thanks for your comment @LPs, declaring an array and passing a pointer to the first element works (the function actually accepts a char * ). The message is printed. Commented Oct 24, 2016 at 10:15
  • C.GoString allocates space as necessary, and it should work in this instance whenever you have a null terminated string. If The string isn't null terminated, or may contain null characters, just treat it as bytes, and use C.GoBytes. Commented Oct 24, 2016 at 12:48
  • @LPs: doesn't the array decay to a pointer in the C function anyway? Commented Oct 24, 2016 at 12:51
  • 1
    @JimB Yes, for sure. It decay to a pointer. But not the contrary: if you use a pointer it has to point to an allocated memory. Commented Oct 24, 2016 at 13:03

1 Answer 1

3

In your first example you are passing a nil pointer, so there is no allocated memory for C.last_error to write the output to (and luckily, it appears to just do nothing).

You need to allocate the memory somehow, and the most straightforward way to do that in Go is to use a slice, rather than create an array with a static size.

buf := make([]byte, ERROR_SIZE)
C.last_error((*C.char)(unsafe.Pointer(&buf[0])), len(buf))

// While C.GoString will find the terminating null if it's there, 
// there's no reason to copy the string in C, and allocate another slice.
if i := bytes.IndexByte(buf, 0); i >= 0 {
    buf = buf[:i]
}

fmt.Printf("message: %s\n", buf)
Sign up to request clarification or add additional context in comments.

1 Comment

Brilliant, thanks @JimB. Appreciate the tip to avoid the copy.

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.