Mark Mark - 1 month ago 7
C Question

How to read a string from a C function into Go?

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?

Answer

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