title: Returning an array of structs in Cython
body: <p>I'm trying to return an array of structs in Cython.</p>
  
  <pre><code>// .pyx
  
  from libc.stdint cimport uint8_t
  
  cdef extern from "&lt;apriltag.h&gt;":
      cdef struct apriltag_detection:
          int id
          double c[2]
          double p[4][2]
  
      ctypedef apriltag_detection apriltag_detection_t
  
  cdef extern from "tag36h11_detector/tag36h11_detector.h":
      apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
  
  cdef class Detection:
      # how do I "link" this to the struct defined above?
      def __cinit__(self):
          pass
      def __dealloc__(self):
          pass
  
  def detect(width, height, frame):
      return scan_frame(width, height, frame)
  </code></pre>
  
  <p>Ideally, I would like to call the <code>detect</code> function in Python code, and get a list of <code>Detection</code> objects where <code>Detection</code> is a wrapper class for the C struct <code>apriltag_detection</code> which is typedef'd to <code>apriltag_detection_t</code>.</p>
  
  <p>I'm getting the following compilation error:</p>
  
  <blockquote>
    <p>tag36h11_detector.pyx:22:21: Cannot convert 'apriltag_detection_t *' to Python object</p>
  </blockquote>
  
  <p>I can't find a reference to returning a pointer to a struct, or an array of structs, anywhere in the documentation.</p>
  
  <p><strong>UPDATE 3</strong></p>
  
  <pre><code>// .h
  typedef struct detection_payload {
      int size;
      apriltag_detection_t** detections;
  } detection_payload_t;
  </code></pre>
  
  <p>I'm trying to convert the above struct into a Python object that holds <code>size</code> and a Python list that holds <code>apriltag_detection_t</code> objects.</p>
  
  <pre><code>// .pyx
  
  cdef extern from "&lt;apriltag.h&gt;":
      cdef struct apriltag_detection:
          int id
          double c[2]
          double p[4][2]
  
      ctypedef apriltag_detection apriltag_detection_t
  
  cdef extern from "tag36h11_detector/tag36h11_detector.h":
      cdef struct detection_payload:
          int size
          apriltag_detection_t** detections
      ctypedef detection_payload detection_payload_t
      detection_payload* scan_frame(int width, int height, uint8_t* data)
  
  ...
  
  
  cdef class Detection:
      cdef apriltag_detection* _d
  
      def __cinit__(self):
          self._d = NULL
      cdef _setup(self, apriltag_detection* d):
          self._d = d
      def __dealloc__(self):
          self._d = NULL
      property id:
          def __get__(self):
              return self._d.id
      property c:
          def __get__(self):
              return self._d.c
      property p:
          def __get__(self):
              return self._d.p
  
  cdef Detection_create(apriltag_detection_t* d):
      return Detection()._setup(d)
  
  cdef class DetectionPayload:
      cdef detection_payload* _p
  
      def __cinit__(self):
          self._p = NULL
      cdef _setup(self, detection_payload* p):
          self._p = p
          self.size = p.size
          self.detections = []
          for i in range(0, self.size):
              apriltag_detection_t* detection = self._p.detections[i]
              d = Detection_create(detection)
              self.detections+=[d]
      def __dealloc__(self):
          _p = NULL
      property size:
          def __get__(self):
              return self.size
      property detections:
          def __get__(self):
              return self.detections
  </code></pre>
  
  <p>I'm getting several syntax errors on the line:</p>
  
  <pre><code>apriltag_detection_t* detection = self._p.detections[I]
  </code></pre>
  
  <p>Specifically, on the pointer <code>apriltag_detection_t*</code></p>
  
  <p><strong>UPDATE 2</strong></p>
  
  <p>This compiles and imports fine now. Still no progress on arrays</p>
  
  <pre><code>from libc.stdint cimport uint8_t
  
  cdef extern from "&lt;apriltag.h&gt;":
      cdef struct apriltag_detection:
          int id
          double c[2]
          double p[4][2]
  
      ctypedef apriltag_detection apriltag_detection_t
  
  cdef extern from "tag36h11_detector/tag36h11_detector.h":
      apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
  
  cdef class Detection:
      cdef apriltag_detection* _d
  
      def __cinit__(self):
          self._d = NULL
      cdef _setup(self, apriltag_detection* d):
          self._d = d
      def __dealloc__(self):
          self._d = NULL
      property id:
          def __get__(self):
              return self._d.id
      property c:
          def __get__(self):
              return self._d.c
      property p:
          def __get__(self):
              return self._d.p
  
  cdef Detection_create(apriltag_detection_t* d):
      return Detection()._setup(d)
  
  def detect(width, height, frame):
      cdef apriltag_detection_t* detection = scan_frame(width, height, frame)
      return Detection_create(detection)
  </code></pre>
  
  <p><strong>UPDATE 1</strong></p>
  
  <p>I tried following the post linked below, and this is what I have so far.</p>
  
  <pre><code>from libc.stdint cimport uint8_t
  
  cdef extern from "&lt;apriltag.h&gt;":
      cdef struct apriltag_detection:
          int id
          double c[2]
          double p[4][2]
  
      ctypedef apriltag_detection apriltag_detection_t
  
  cdef extern from "tag36h11_detector/tag36h11_detector.h":
      apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
  
  cdef class Detection:
      cdef apriltag_detection* _d;
      def __cinit__(self):
          self._d = NULL
      def _setup(self, apriltag_detection* d):
          self._d = d
      def __dealloc__(self):
          self._d = NULL
      property id:
          def __get__(self):
              return self._t.id
      property c:
          def __get__(self):
              return self._t.c
      property p:
          def __get__(self):
              return self._t.p
  
  cdef Detection_create(apriltag_detection_t* d):
      return Detection()._setup(d)
  
  def detect(width, height, frame):
      return &lt;Detection&gt;scan_frame(width, height, frame)
  </code></pre>
  
  <p>Although this is closer than I was before, I'm still getting the error:</p>
  
  <blockquote>
    <p>tag36h11_detector.pyx:33:30: Cannot convert 'apriltag_detection_t *' to Python object</p>
  </blockquote>
  
  <p>On the line</p>
  
  <pre><code>cdef Detection_create(apriltag_detection_t* d):
      return Detection()._setup(d)
  </code></pre>
  
  <p>Moreover, I have no idea how to return a Python list...</p>