Search code examples
pointersgocgo

convert array of strings from CGO in GO


Can I convert an array of strings (char**) returned from a C (cgo) function in Go? The code below compiles and runs, but I'm unable to range through a list of strings.
And I'm not even sure if it breaks the rules on "passing pointers": https://golang.org/cmd/cgo/ Any thoughts would be helpful, it's been years since I coded in C! Thanks in advance!

package main
/*
#include "stdlib.h"

char** getlist ()
{
    char **array = NULL;
    array = (char**)realloc(array, 2*sizeof(*array));
    array[0]="HELLO";
    array[1]="WORLD";
    return array;
}

*/
import "C"

import (
    "log"
    "unsafe"
)

func main() {
    list := C.getlist();
    log.Printf("\n========\n C.getList()=%s", list)
    ulist := unsafe.Pointer(list)
    log.Printf("\n========\nulist=%s", ulist)
}

Solution

  • In order to iterate over the strings in Go, you need to convert the array to a Go slice. We can skip allocation here, and convert it directly (your example statically sets the length to 2, but in practice you would likely have another source for this size)

    cSlice := (*[1 << 28]*C.char)(unsafe.Pointer(list))[:2:2]
    

    We can iterate over this directly, and use the C.GoString function to convert the C strings. This is safer to store since it's copying the data to Go memory, but if this slice were exceptionally large we could save the allocation with the same unsafe conversion as above, though you would first need to find the length of each string.

    var slice []string
    for _, s := range (*[1 << 28]*C.char)(unsafe.Pointer(list))[:2:2] {
        slice = append(slice, C.GoString(s))
    }