假设我正在与来自Go的第三方C库进行交互,以维护一些复杂的数据结构。它允许我使用看起来像这样的API迭代数据结构:
typedef void (*callback)(double x, void *params);
iterate(int64_t start_idx, int64_t end_idx, callback f, void *params);
理念是iterate
遍历start_idx
和end_idx
之间的每个索引,并在该范围内的每个元素上调用f
。如果您想从复杂的数据结构中读取数据并将其读入数组,您可以编写如下内容:
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++;
}
现在让我们假设我想在Go函数中包装此API调用,其中用户将函数传递给预先分配的缓冲区。在Go 1.5中,我可能做过这样的事情:
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))
}
然而,在Go 1.6中,这是无效的。 data
是一个Go指针,因此是buf
,这意味着运行时会发生混乱以防止GC错误。也不允许将buf
分配为C指针。我认为处理这类事情的预期方法是将data
分配为C指针,但我不想在Read
内分配临时数组,因为这些数组足够大,我不能同时在内存中保存其中两个(即使我可以,我也无法处理堆碎片)。
我当前的(非常hacky)解决方案要么将一些数据作为全局变量传递,要么将i
和数组长度打包在数组的第一个元素中(然后在数据的第一个元素中交换一些数据)迭代结束)。有没有办法让我这样做不那么糟糕?