Use Go slice in C

生来就可爱ヽ(ⅴ<●) 提交于 2021-02-07 10:36:31

问题


I'm trying to build a go shared library with a function that returns a slice.
How can I use the slice from C code ?

package main

import "C"

type T struct {
    A C.int
    B *C.char
}

//export Test
func Test() []T {
    arr := make([]T, 0)
    arr = append(arr, T{C.int(1), C.CString("a")})
    arr = append(arr, T{C.int(2), C.CString("abc")})
    return arr
}

func main() {}

go build -o lib.so -buildmode=c-shared main.go

I now have a lib.so and a lib.h

What would be the C code to print the values of the array ?

#include <stdio.h>
#include "lib.h"

typedef struct {
  int   A;
  char* B;
} T;

int main() {
  GoSlice a = Test();
  for (int i = 0; i < 2; i++){
    printf("%s\n", ((T *)a.data)[i].B);
  }
}

gcc -o main main.c ./lib.so


回答1:


To start, C doesn't know about the Go slice type, nor how to manipulate it, so you should return an array pointer just like you would in C.

You also can't allocate the slice in Go then return a pointer to the allocated memory without some way to keep it from being GC'ed. In this example it's probably simpler to just allocate the array in C.

p := C.malloc(C.size_t(2))

// make a slice for convenient indexing
arr := (*[1 << 30]*T)(p)[:2:2]
arr[0] = &T{C.int(1), C.CString("a")}
arr[1] = &T{C.int(2), C.CString("abc")}

return p

You will also of course need to handle the array size, which you could do with by combining it into a struct like you did for strings, or accept a second pointer parameter to set to the return size.

Don't forget that since you are allocating the memory in C, you are also responsible for freeing it. This goes for both the malloc call, as well as for the C.CString arrays.




回答2:


Use Go slice in C


Here's an example of C printing a Go byte slice:

package main

/*
#include <stdio.h>

void printbuf(size_t len, unsigned char *buf) {
    printf("%lu [", len);
    if (!buf) {
        len = 0;
    }
    size_t maxwidth = 16;
    size_t width = len <= maxwidth ? len : maxwidth;
    for (size_t i = 0; i < width; i++) {
        if (i > 0) {
            printf(" ");
        }
        printf("%02X", buf[i]);
    }
    if (width < len) {
        printf(" ...");
    }
    printf("]\n");
}
*/
import "C"

func cbuf(buf []byte) (size C.size_t, ptr *C.uchar) {
    var bufptr *byte
    if cap(buf) > 0 {
        bufptr = &(buf[:1][0])
    }
    return C.size_t(len(buf)), (*C.uchar)(bufptr)
}

func main() {
    buf := make([]byte, 24, 32)
    for i := range buf {
        buf[i] = byte(i)
    }
    bufsize, bufptr := cbuf(buf)
    C.printbuf(bufsize, bufptr)
}

Output:

24 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]

func Test() []T has the wrong return values for C. Return a length and an array pointer, like cbuf.



来源:https://stackoverflow.com/questions/51525876/use-go-slice-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!