简体   繁体   中英

Is there a preferred way to interface with c++ std::array types in Cython?

Cython's libcpp has wrappers for containers like std::vector , but still nothing similar for std::array . As far as I understand, it's because Cython does not have support for non-type template arguments, so there is no nice way to create a .pxd for arrays of arbitrary size ( See here , and here for existing discussion). Since these posts are from several years ago, I am wondering if there has been any progress in preferred ways to handle std::array types?

For a concrete example, here's something I'm interested in doing. I have a c++ struct that contains an array type:

%% ex.hpp
#include <array>

struct example{
    double a;
    int b;
    std::array<double, 3> c;
};

and I want to make a cdef class that exposes c with a getter and setter so I can set it with a python list or numpy array:

cdef extern from "ex.hpp":
    cdef cppclass example:
        double a
        int b
        <???> c # Not sure what the best option is here

cdef class pyExample:
    cdef example *_cppExample

    def __cinit__(self):
        self._cppExample= new example()

    def __dealloc(self):
        del self._cppExample

    @property
    def c(self):
        return self._cppExample

    @c.setter
    def c(self, pyArr):
        self._cppExample.c = pyArr

Seems there is not much progress on this front. At any rate, the simple example I showed actually poses a slightly different problem of how to get/set std:array types contained within an extension type from python. Using the links in the question, I adapted a solution like this:

%% example.pyx

cdef extern from "<array>" namespace "std" nogil:
    cdef cppclass arrayd3 "std::array<double, 3>":
        arrayd3() except +
        double& operator[](size_t)

cdef arrayd3ToNumpy(arrayd3 arr):
    cdef double[::1] view = <double[:3]>(&arr[0])
    return np.asarray(view.copy())

cdef arrayd3 numpyToArrayd3(nparr):
    nparr = np.asarray(nparr, dtype=np.double)
    cdef double[:] view = memoryview(nparr)
    cdef arrayd3 *arr = <arrayd3 *>(&view[0])
    return dereference(arr)

The first block gives cython access to std::array for a specific type and size (in this case std::array<double, 3> is named arrayd3) , the second block provides a function for converting type arrayd3 to a numpy array by first casting it to a memory view, and the third block provides a function for converting an array-like python object to an arrayd3 type also via memory views.

Then the example becomes

cdef extern from "ex.hpp":
    cdef cppclass example:
        double a
        int b
        arrayd3 c

cdef class pyExample:
    cdef example *_cppExample

    def __cinit__(self):
        self._cppExample= new example()

    def __dealloc(self):
        del self._cppExample

    @property
    def c(self):
        return arrayd3ToNumpy(self._cppExample)

    @c.setter
    def c(self, pyArr):
        self._cppExample.c = numpyToArrayd3(pyArr)

Of course you need to do all of this for every combination of array type and size so it's less than ideal. And I'm answering my own question so I'm fumbling in the dark here, if there are other solutions please contribute!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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