简体   繁体   English

使用boost :: python将数据缓冲区导入C ++

[英]Getting buffer of data into C++ with boost::python

I'd like to be able to pass a buffer (ie something implementing the buffer protocol, such as a numpy array) of numeric data into c++ from Python: 我希望能够将数值数据的缓冲区(即,实现缓冲区协议的某些内容,例如numpy数组)从Python传递到c ++中:

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

and receive it in c++ in some way: 并以某种方式用c ++接收它:

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

prints 版画

1 
2
3

I don't really care what the [something] is (pointer, std::vector, whatever). 我真的不在乎[东西]是什么(指针,std :: vector等)。 Does anyone know how to do this? 有谁知道如何做到这一点? There's surprisingly little information on it... 令人惊讶的是,关于它的信息很少。

OK <get_ready_for_this.mp3> here's what I did to solve this. 确定<get_ready_for_this.mp3>这是我为解决此问题所做的工作。

First, I created a type representing the buffer I wanted, along with some helpers functions to cast a buffer of data to the target format. 首先,我创建了一个表示所需缓冲区的类型,以及一些帮助程序函数,将数据缓冲区转换为目标格式。 You could easily amend this to be more flexible, but I just want an array of complex floating point values. 您可以轻松地对此进行修改以使其更加灵活,但是我只想要一个复杂的浮点值数组。

// 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);
    }
}

Then, I built a custom converter from python to my cbuffer type: 然后,我构建了一个从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;
    }
};

The convertible() function checks that the Python object implements the buffer protocol. convertible()函数检查Python对象是否实现了缓冲区协议。 Then the construct() function actually extracts a buffer from the object, and converts it to the desired format via the above helper functions. 然后, construct()函数实际上从对象中提取了一个缓冲区,并通过上述辅助函数将其转换为所需的格式。 If we fail at any step, cleanup and throw a runtime exception. 如果我们在任何步骤都失败,请清理并抛出运行时异常。

Lastly we instantiate the converter in the module: 最后,我们在模块中实例化转换器:

// define python module
BOOST_PYTHON_MODULE(module) {
    // register python -> c++ converters
    python_to_cbuffer();

    def("test", test);
}

And, if we create a test function: 并且,如果我们创建一个测试函数:

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

Then in python: 然后在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)

Enjoy! 请享用!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM