如何处理在Go 1.6的CGo指针规则下保存大缓冲区的void *参数列表

时间:2016-06-07 16:44:27

标签: go cgo

假设我正在与来自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_idxend_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和数组长度打包在数组的第一个元素中(然后在数据的第一个元素中交换一些数据)迭代结束)。有没有办法让我这样做不那么糟糕?

0 个答案:

没有答案