Struct definition with typed memoryview as member

2019-05-10 19:54发布

问题:

Currently I am trying to get a struct with a typed memoryview to work. E.g.

ctypedef struct node:
    unsigned int[:] inds

If inds is not a memoryview it works flawlessly as far as I can see. However, with a memoryview and using something like

def testFunction():
    cdef node testNode   
    testNode.inds = numpy.ones(3,dtype=numpy.uint32)

I get a Segmentation fault. Is there anything obvious I am overlooking or do typed memoryviews not work in structs?


EDIT:

So I rewrote the example a little bit (due to larsman's questions) to hopefully clarify the problem a little bit:

def testFunction():
    cdef node testNode
    cdef unsigned int[:] tempInds
    tempInds = numpy.ones(3,dtype=numpy.uintc)
    testNode.inds = tempInds

Only the last line produces a segfault. I don't know how to check the problem in more detail. For me it currently just looks like the structs can't handle typed memory views... But hopefully I am missing something.

I am using Cython 0.20 (just updated now, previously 0.19 gave the same error). Updated Numpy to 1.8.0 as well.


EDIT 2:

If anyone with similar problems reads this in the future: I searched quite a lot by now and can't get it to work. As everything else is working except for memory views I guess it is not (easily) possible. I intended to use memory views due to the convenient slicing of multidimensional arrays, but apparently they don't work in structs. Now I am saving a C pointer in the struct and cast them to a memory view if I need slicing... One has to be very careful that the arrays are accessed correctly, but in my case it seems to work. If there are no resolving answers in this thread in a couple of weeks from now I am maybe doing a feature request for Cython.

回答1:

The problem comes from uninitialized memory because of Cython lack of constructors for primitive cdefed structs. When testNode.inds = tempInds assignment is made Cython tries to release testNode.inds which was not even initialized properly and dereferences random pointer.

This is indeed bug in Cython (please file!) and there's currently no solution (to my knowledge), but here's an ugly hack that works.

from libc.string cimport memset

ctypedef struct node:
    int dummy_hack
    unsigned int[:] inds

def testFunction():
    cdef node testNode
    cdef unsigned int[:] tempInds
    tempInds = numpy.ones(3,dtype=numpy.uintc)
    # zero init memory to avoid deref garbage
    memset(&testNode.dummy_hack, 0, sizeof(void*)*4)
    testNode.inds = tempInds

FYI. Actual memory view object in C++ looks like this (on Cython 21.2):

MemoryView object is 
__Pyx_memviewslice struct typedef struct {
  struct __pyx_memoryview_obj *memview;
  char *data;
  Py_ssize_t shape[8];
  Py_ssize_t strides[8];
  Py_ssize_t suboffsets[8];
};