我有一个返回std::vector
的C ++函数,并且使用Pybind11,我想将该矢量的内容作为Numpy数组返回,而不必将矢量的基础数据复制到原始数据数组中
当前尝试
在this well-written SO answer中,作者演示了当Numpy数组的引用计数为零时,如何确保适当释放用C ++创建的原始数据数组。我尝试使用std::vector
来编写此版本:
// aside - I made a templated version of the wrapper with which
// I create specific instances of in the PYBIND11_MODULE definitions:
//
// m.def("my_func", &wrapper<int>, ...)
// m.def("my_func", &wrapper<float>, ...)
//
template <typename T>
py::array_t<T> wrapper(py::array_t<T> input) {
auto proxy = input.template unchecked<1>();
std::vector<T> result = compute_something_returns_vector(proxy);
// give memory cleanup responsibility to the Numpy array
py::capsule free_when_done(result.data(), [](void *f) {
auto foo = reinterpret_cast<T *>(f);
delete[] foo;
});
return py::array_t<T>({result.size()}, // shape
{sizeof(T)}, // stride
result.data(), // data pointer
free_when_done);
}
已观察到的问题
但是,如果我从Python调用它,我会观察到两件事:(1)输出数组中的数据是垃圾,(2)手动删除Numpy数组时,我收到以下错误(SIGABRT):
python3(91198,0x7fff9f2c73c0) malloc: *** error for object 0x7f8816561550: pointer being freed was not allocated
我的猜测是,此问题与“ delete[] foo
”行有关,大概是在将foo
设置为result.data()
的情况下调用的。这不是解除分配std::vector
的方法。
可能的解决方案
一种可能的解决方案是创建一个T *ptr = new T[result.size()]
并将result
的内容复制到此原始数据数组。但是,在某些情况下,结果可能会很大,因此我想避免花费所有时间来分配和复制。 (但也许没有我想的那么长。)
我对std::allocator
也不了解,但是也许有一种方法可以分配compute_something_returns_vector()
函数调用的输出向量 outside 所需的原始数据数组然后丢弃std::vector
,然后保留基础原始数据数组?
最后的选择是重写compute_something_returns_vector
。
答案 0 :(得分:2)
与同事进行离线讨论后,我解决了我的问题。我不想冒充别人,所以我不会接受自己的回答。但是,为了将SO用作信息目录,我想在这里为其他人提供答案。
问题很简单:result
是堆栈分配的,需要进行堆分配,以便free_when_done
可以拥有所有权。以下是修复示例:
{
// ... snip ...
std::vector<T> *result = new std::vector<T>(compute_something_returns_vector(proxy));
py::capsule free_when_done(result, [](void *f) {
auto foo = reinterpret_cast<std::vector<T> *>(f);
delete foo;
});
return py::array_t<T>({result->size()}, // shape
{sizeof(T)}, // stride
result->data(), // data pointer
free_when_done);
}
我还能够使用std::unique_ptr
实现解决方案,而无需使用free_when_done
函数。但是,我无法使用任何一种解决方案运行Valgrind,因此我不能100%确定该向量所保存的内存是否已适当释放。 (Valgrind + Python对我来说是个谜。)为完整起见,下面是std::unique_ptr
方法:
{
// ... snip ...
std::unique_ptr<std::vector<T>> result =
std::make_unique<std::vector<T>>(compute_something_returns_vector(proxy));
return py::array_t<T>({result->size()}, // shape
{sizeof(T)}, // stride
result->data()); // data pointer
}
但是,我能够检查在Python和C ++代码中分配的向量的地址,并确认没有复制compute_something_returns_vector()
的输出。