5

Background: using cgo to call C functions from Golang.

I want to use a C function which has this signature: int f(int *count, char ***strs). It will modify the data of count and strs, which is the reason why it uses pointer to them. The value of count is the length of strs; strs is an array of string; the return value is simply an (boolean) indicator which states whether there is an error or not.

In golang, I can successfully pass and modify count by using C.f((*C.int)(&count)); pass []string by using []*C.char. Sample code is like this:

/*
#include <stdio.h>
int f(int *c, char **str) {
    int i;
    printf("%d\n", *c);
    for (i = 0; i < *c; i++) {
        printf("%s\n", str[i]);
    }
    *c = (*c) + 1;
    return 1;
}
*/
import "C"
func go_f(strs []string) int {
    count := len(strs)
    c_count := C.int(count)

    c_strs := make([]*C.char, count)
    for index, value := range strs {
        c_strs[index] = C.CString(value)
        defer C.free(unsafe.Pointer(c_strs[index]))
    }

    err := C.f(&c_argc, (**C.char)(&c_argv[0]))
    return int(err)
}

As you can see, the C function is currently int f(int *c, char **str), but what I'd like is int f(int *c, char ***str).

This is to say: what I actually want is to enable the modification to the string array (e.g. resize) in C and turn it back to a Go string slice so I can still use it in Go.

How to do this? I've searched and experimented for a while but with no luck.

renyuneyun
  • 608
  • 5
  • 18
  • 1
    are you sure that memory allocated in main program can be reallocated in C function? It can be differences in memory management – VolAnd Mar 01 '17 at 11:34
  • Instead of resorting to three star programming, have the function return a `char**`. The return code of the function does nothing meaningful currently. – Lundin Mar 01 '17 at 15:36
  • @VolAnd The current working part (with `char**`) is correct. However, I'm not sure what to do for my target (`char***`). – renyuneyun Mar 01 '17 at 17:16
  • @Lundin This is only a sample... not what my actually code does – renyuneyun Mar 01 '17 at 17:19

1 Answers1

6

A Go slice is both allocated in Go, and a different data structure than a C array, so you can't pass it to a C function (cgo will also prevent you from doing this because a slice contains a Go pointer)

You need to allocate the array in C in order to manipulate the array in C. Just like with C.CString, you will also need to track where to free the outer array, especially if the C function may possibly allocate a new array.

cArray := C.malloc(C.size_t(c_count) * C.size_t(unsafe.Sizeof(uintptr(0))))

// convert the C array to a Go Array so we can index it
a := (*[1<<30 - 1]*C.char)(cArray)
for index, value := range strs {
    a[index] = C.CString(value)
}

err := C.f(&c_count, (***C.char)(unsafe.Pointer(&cArray)))
JimB
  • 104,193
  • 13
  • 262
  • 255
  • I'm still experimenting how to correctly get go string from `a` - simply using `C.GoString(a[0])` doesn't produce the correct result. But...what does the magic number 30 do? – renyuneyun Mar 01 '17 at 17:34
  • @renyuneyun: the "magic number" isn't 30, it's `1<<30-1`, or 1073741823. It's just a large array size, since array types are static. – JimB Mar 01 '17 at 17:40
  • Thanks a lot :) (Yeah, I'm aware that's bitwise operation.) I just happen to see that value all around and am wondering why it's 2^30 rather than other values. Regarding getting go strings, assigning `a` another time to get the new pointer value of `cArray` would produce successful results. – renyuneyun Mar 01 '17 at 17:48
  • @renyuneyun: Ah ok, in that case the 30 is because `2^30 - 1` is the largest array size that can be used on all architectures. – JimB Mar 01 '17 at 17:55