Cgo fixed length array woes

534 views
Skip to first unread message

mark mellar

unread,
Jan 28, 2020, 7:43:36 AM1/28/20
to golang-nuts
Hi Folks, I'm having some trouble instantiating a C struct from a 3rd party API.

I'm not sure if this is more a C question or a Go question, details in the code below, any help would be much appreciated!

M

// This non-compiling example demonstrates an issue I'm having integrating a 3rd party C api with my Go code
// The comment preceding 'import "C"' constitutes C code which is interpreted as a header when compiling.  
// 'api_struct' represents the 3rd party struct I'm attempting to instantiate and populate in my Go Code
//    we cannot make changes to this struct 
// other c functions in the comment are helper functions I've written to instantiate certain C types

// The go code defines an array of strings and an array of ints. For each of these arrays a helper
// function is called to create a C array of the C equivalent type before populating that array.
// Once C arrays have been populated they are assigned to fields in the a new api_struct.

// This method works fine for the string array, but compile fails when we get to the int array
// This is because makeIntArray returns *_Ctype_int but api_struct.rLimits expects [12]_Ctype_int. Here's the error...
// cannot use cInts (type *_Ctype_int) as type [12]_Ctype_int in field value

// I'd like to modify makeIntArray to return a [12]_Ctype_int but I can't seem to find the right syntax to achieve this
package main

/*
#define  ARRLEN 12
#include <stdlib.h>

struct api_struct {
char    **askedHosts;
int     rLimits[ARRLEN];
};

static char**makeCharArray(int size) {
        return calloc(sizeof(char*), size);
}

static void setArrayString(char **a, char *s, int n) {
        a[n] = s;
}

static void freeCharArray(char **a, int size) {
        int i;
        for (i = 0; i < size; i++)
                free(a[i]);
        free(a);
}

static int*makeIntArray() {
        int* p = malloc(sizeof(p) * ARRLEN);
        return p;
}

static void setArrayInt(int *a, int s, int n) {
        a[n] = s;
}

static void freeIntArray(int *a, int size) {
        free(a);
}


*/
import "C"
import "fmt"

func main() {
goHosts := []string{"host1", "host2", "host3"}
cHosts := C.makeCharArray(C.int(len(goHosts)))
        for i, s := range goHosts {
                C.setArrayString(cHosts, C.CString(s), C.int(i))
        }

goLimits := []int{1,2,3,4,5,6,7,8,9,10,11,12}
cInts := C.makeIntArray()
        for i, s := range goLimits {
                C.setArrayInt(cInts, C.int(s), C.int(i))
        }

s := C.struct_api_struct{
askedHosts: cHosts,
rLimits: cInts,
}
fmt.Printf("%+v\n", s)
}



Ian Lance Taylor

unread,
Jan 28, 2020, 10:21:55 AM1/28/20
to mark mellar, golang-nuts
The easy, and likely more efficient, way is to just set the rLimits
field element by element.

There is no safe way to convert from a *C.int to a [12]C.int in Go.
You can do it easily enough using unsafe.

a := *(*[12]C.int)(unsafe.Pointer(p))

This is of course only safe if p does in fact point to 12 C.int values.

Ian

mark mellar

unread,
Jan 29, 2020, 12:32:23 PM1/29/20
to golang-nuts
Thanks Ian!

My C's a bit rusty, I'd expected I'd have to explicitly allocate some memory for rLimits, similar to askedHosts, turns out that isn't required after all and your suggestion of setting element by element is working like a charm!

Thanks again,

Mark
Reply all
Reply to author
Forward
0 new messages