使用 boost::p ython 将数据缓冲区放入C++中

Getting buffer of data into C++ with boost::python

本文关键字:C++ 缓冲区 boost ython 使用 数据      更新时间:2023-10-16

我希望能够将数字数据的缓冲区(即实现缓冲区协议的东西,例如numpy数组(从Python传递到C ++中:

>>> import mymod
>>> import numpy
>>> mymod.some_func(numpy.array([1,2,3]))

并以某种方式在 C++ 中接收它:

void some_func([something] array) {
for (int ii : array) {
cout << ii << endl;
}
}

指纹

1 
2
3

我真的不在乎[某物]是什么(指针,std::vector,等等(。 有谁知道如何做到这一点? 令人惊讶的是,关于它的信息很少...

好的<get_ready_for_this.mp3>这是我为解决这个问题所做的。

首先,我创建了一个表示所需缓冲区的类型,以及一些用于将数据缓冲区转换为目标格式的帮助程序函数。 您可以轻松地将其修改为更灵活,但我只想要一个复杂的浮点值数组。

// vector of complex values
typedef vector<cfloat> cbuffer;

// helper to copy data
template<typename T>
void cbuffer_copy_from(cbuffer& cbuf, void *ptr, ssize_t len, ssize_t stride) {
cbuf.reserve(len);
// convert elements into buffer
char* cptr = (char*)ptr;
for (ssize_t ii=0; ii < len; ii++) {
cbuf.emplace_back(*reinterpret_cast<T*>(cptr));
cptr += stride;
}
};

// populate vector from source
template<typename T>
void cbuffer_from(cbuffer& cbuf, void *ptr, ssize_t len, ssize_t stride) {
cbuffer_copy_from<T>(cbuf, ptr, len, stride);
}

// fast path for data that's already cfloat
template <>
void cbuffer_from<cfloat>(cbuffer& cbuf, void *ptr, ssize_t len, ssize_t stride) {
// if stride is right, we can just copy the data
if (stride == sizeof(cfloat)) {
cbuf.resize(len);
memcpy(&cbuf[0], ptr, len*sizeof(cfloat));
} else {
cbuffer_copy_from<cfloat>(cbuf, ptr, len, stride);
}
}

然后,我构建了一个从python到我的cbuffer类型的自定义转换器:

// python -> cbuffer conversion
struct python_to_cbuffer {
// register converter 
python_to_cbuffer() {
converter::registry::push_back(
&convertible,
&construct,
type_id<cbuffer>()
);
}
// does python object implement buffer protocol?
static void* convertible(PyObject* object) {
return PyObject_CheckBuffer(object) ? object : nullptr;
}
// convert object into a complex number
static void construct(
PyObject* object,
converter::rvalue_from_python_stage1_data* data
) {
// grab pointer to memory into which to construct the new value
void* storage = ((converter::rvalue_from_python_storage<cbuffer>*)data)->storage.bytes;
// create buffer object from export source, require format
Py_buffer view;
if (PyObject_GetBuffer(object, &view, PyBUF_FORMAT | PyBUF_STRIDES) < 0) {
return;
}
// make sure it's a one dimensional array
if (view.ndim != 1) {
PyBuffer_Release(&view);
throw std::runtime_error("Array object is not one dimensional");
}
// build new cbuffer to store data
new (storage) cbuffer;
cbuffer* buffer = static_cast<cbuffer*>(storage);
// try to convert view data into cfloat format
string type(view.format);
if (type == "f")  cbuffer_from<float>  (*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "d")  cbuffer_from<double> (*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "Zf") cbuffer_from<cfloat> (*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "Zd") cbuffer_from<cdouble>(*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "b")  cbuffer_from<int8_t> (*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "h")  cbuffer_from<int16_t>(*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "i")  cbuffer_from<int32_t>(*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "l")  cbuffer_from<int32_t>(*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "q")  cbuffer_from<int32_t>(*buffer, view.buf, view.shape[0], view.strides[0]);
else if (type == "n")  cbuffer_from<ssize_t>(*buffer, view.buf, view.shape[0], view.strides[0]);
else {
buffer->~cbuffer();
throw std::runtime_error("Unable to marshall '" + string(view.format) + "' data format");
}
// Stash the memory chunk pointer for later use by boost.python
data->convertible = storage;
}
};

convertible()函数检查 Python 对象是否实现了缓冲区协议。 然后construct()函数实际上从对象中提取缓冲区,并通过上述帮助程序函数将其转换为所需的格式。 如果我们在任何步骤失败,请清理并抛出运行时异常。

最后,我们在模块中实例化转换器:

// define python module
BOOST_PYTHON_MODULE(module) {
// register python -> c++ converters
python_to_cbuffer();
def("test", test);
}

而且,如果我们创建一个测试函数:

void test(cbuffer buf) {
for (cfloat val : buf) {
printf("(%f, %f)n", val.re, val.im);
}
}

然后在 python 中:

>>> module.test(numpy.array([1+2j,3+4j],dtype=numpy.complex64))
(1.000000, 2.000000)
(3.000000, 4.000000)
>>> module.test(numpy.array([1,2],'b'))
(1.000000, 0.000000)
(2.000000, 0.000000)
>>> module.test(numpy.array([1,2],'i'))
(1.000000, 0.000000)
(2.000000, 0.000000)
>>> module.test(numpy.array([1,2],'l'))
(1.000000, 0.000000)
(2.000000, 0.000000)

享受!