0

I'm trying to call a C library from GO using cgo.

The C library has the following function:

int receive(void** data);

// I'd call it like that:
void* myptr; // myptr=null
int nbBytes = receive(&myptr);
if (nbBytes==0) { return }
// myptr has now an address to a valid buffer that contains nbBytes bytes.

// And then casting it with char* to access the data that can be anything. Example:
printf("%d", *(char*)myptr);

How can I call this receive() function from GO? Go doesn't allocate any memory, the memory address is returned via myptr and directly access from it.

  • receive() is a "no-copy" and writes the actual data's address into myptr. The data is then accessed using *(char*)myptr.
  • we can assume receive() allocates and frees the buffer, it's hidden from the lib's user

Ideally, I would read the data via []byte in go.

Alexis
  • 2,136
  • 2
  • 19
  • 47

1 Answers1

2

[Edit: you added a clarification that says this isn't just suggested. But we still don't know how the data are used afterward, from C code.]

There is not enough information in your question to answer it completely, because we don't know—the C language alone doesn't tell us—how this void ** is used. Your comments and additional code suggest (pretty strongly) that the way it's used is that receive fills in the pointer:

int receive(void **data) {
    *data = <something>;
    return <some value>;
}

where the angle-bracketed sections are unknown to us; to use this data from C code, we'd do just what you said:

void f(void) {
    void *p;
    int ret;
    ...
    ret = receive(&p);
}

What we don't know, given this much and the (justifiable) assumptions, are:

  • What does the ret value indicate?
  • Is p always valid afterward?
  • How many bytes at *p are accessible?

For instance, would:

struct our_data dst;
memcpy(&dst, p, len);

be a valid way to get the bytes from p into the data-structure dst, and if so, where does the length len come from? Is it implied, e.g., do we know that because ret was not -1 (error) that p is valid and has sizeof(struct our_data) bytes available, and we need the memcpy just to make it properly aligned?

If we knew all of these things, we'd be able to do the job directly from Go:

var p unsafe.Pointer
var obj C.struct_our_data
ret := C.receive(&p)
C.memcpy(unsafe.Pointer(&obj), p, len) // or similar with copy()

although it might—depending on task etc—make more sense to just write a deserializer for the raw data, which we obtain as an array living in C memory; see, e.g., How to convert [1024]C.char to [1024]byte.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you! I edited my question, please comment if that's not enough. – Alexis Dec 28 '21 at 09:14
  • How do you know how long the accessible data are? – torek Dec 28 '21 at 09:17
  • yes, it's handled via a different function. My question is regarding the void** and cgo. From that `void*` I need a `[]byte`, either no-copy from the C pointer or by copying the data. – Alexis Dec 28 '21 at 10:13
  • OK - see the linked question. If the length is variable, you use the "convert to giant array and then take a slice of it" method. If it's a constant, convert to an array of the right size (and then slice or not—it's up to you). – torek Dec 28 '21 at 10:17
  • That is, we'll use the `mySlice := (*[1 << 30]byte)(unsafe.Pointer(&C.my_buf))[:int(C.BUFF_SIZE):int(C.BUFF_SIZE)]` but instead of `unsafe.Pointer(&var)` you already just have `p`. The `[1 << 30]` just has to be "at least as big as the actual length". – torek Dec 28 '21 at 10:18