Suppose I'm interacting with a third party C library from Go that maintains some complicated data structure. It allows me to iterate over the data structure using an API that looks something like this:
typedef void (*callback)(double x, void *params);
iterate(int64_t start_idx, int64_t end_idx, callback f, void *params);
With the idea being that iterate
loops over every index between start_idx
and end_idx
and calls f
on every element in that range. If you wanted to read the data out of the complicated data structure and into an array, you would write something like this:
typedef struct buffer {
double *data;
int64_t i;
} buffer;
void read_callback(double x, void *params) {
buffer *buf = (buffer*) params;
buf->data[i] = x;
buf->i++;
}
Now let's suppose that I wanted to wrap this API call in a Go function where the user passes the function a pre-allocated buffer. In Go 1.5, I might have done something like this:
func Read(startIdx, endIdx int64, data []float64) {
buf := &C.buffer{}
buf.data = unsafe.Pointer(&data[0])
C.iterate(C.int64_t(startIdx), C.int64_t(endIdx),
(C.callback)(C.read_callback), unsafe.Pointer(buf))
}
However, in Go 1.6 this is invalid. data
is a Go-pointer and so is buf
, meaning that the runtime panics to prevent a GC error. Allocating buf
as a C-pointer is also not allowed. I think the intended way to handle something like this would be to allocate data
as a C pointer, but I don't want to allocate a temporary array inside Read
because these arrays are large enough that I can't hold two of them in memory at once (and even if I could, I wouldn't be able to deal with the heap fragmentation).
My current (very hacky) solutions are to either pass some of the data around as global variables or to pack i
and the array length in the first element of array (and then to swap some data around at the end of the iteration). Is there a way for me to do this which is less terrible?