Search code examples
structcythontypedef

Cython: external struct definition throws compiler error


I am trying to use Collections-C in Cython.

I noticed that some structures are defined in the .c file, and an alias for them is in the .h file. When I try to define those structures in a .pxd file and use them in a .pyx file, gcc throws an error: storage size of ‘[...]’ isn’t known.

I was able to reproduce my issue to a minimum setup that replicates the external library and my application:

testdef.c

/* Note: I can't change this */
struct bogus_s {
    int x;
    int y;
};

testdef.h

/* Note: I can't change this */
typedef struct bogus_s Bogus;

cytestdef.pxd

# This is my code
cdef extern from 'testdef.h':
    struct bogus_s:
        int x
        int y

    ctypedef bogus_s Bogus

cytestdef.pyx

# This is my code
def fn():
    cdef Bogus n

    n.x = 12
    n.y = 23

    print(n.x)

If I run cythonize, I get

In function ‘__pyx_pf_7sandbox_9cytestdef_fn’:
cytestdef.c:1106:9: error: storage size of ‘__pyx_v_n’ isn’t known
Bogus __pyx_v_n;
      ^~~~~~~~~

I also get the same error if I use ctypedef Bogus: [...] notation as indicated in the Cython manual.

What am I doing wrong?

Thanks.


Solution

  • Looking at the documentation for your Collections-C library these are opaque structures that you're supposed to use purely through pointers (don't need to know the size to have a pointer, while you do to allocate on the stack). Allocation of these structures is done in library functions.

    To change your example to match this case:

     // C file
     int bogus_s_new(struct bogus_s** v) {
         *v = malloc(sizeof(struct bogus_s));
         return (v!=NULL);
     }
    
     void free_bogus_s(struct bogus_s* v) {
          free(v);
     }
    

    Your H file would contain the declarations for those and your pxd file would contain wrappers for the declarations. Then in Cython:

    def fn():
        cdef Bogus* n
        if not bogus_s_new(&n):
            return
        try:
            # you CANNOT access x and y since the type is
            # designed to be opaque. Instead you should use
            # the acessor functions defined in the header
            # n.x = 12
            # n.y = 23
        finally:
            free_bogus_s(n)