Wrapping a typedefed enum in cython

2019-05-27 02:27发布

问题:

I would like to wrap the following code in cython:

enum Status {GOOD, BAD};

typedef enum Status STATUS;
// note that the typedef means people dont
// have to write `enum Status` everywhere

// just returns `GOOD`
STATUS hello();

I wrote the following cython code in c_library.pxd:

cdef extern from "library.h":

  cpdef enum Status:
    GOOD,
    BAD

  ctypedef Status STATUS

  cpdef STATUS hello()

The module c_library now contains c_library.GOOD, c_library.BAD, and c_library.Status, which behaves like an enum. However, the return value of a call to function hello returns a plain int:

>>> c_library.hello()
0
>>> type(c_library.hello())
<class 'int'>

I would like the result to be wrapped in an enum of the same type as well. I can change the cython file, but not the underlying C code. Is that possible?

回答1:

That looks like a hiccup (minor bug?) of Cython, which decides to use __Pyx_PyInt_From_enum__ for some reasons when wrapping cdef-function into a def function.

As a quick workaround I can propose to explicitly create a Status-enum:

%%cython
cdef extern from *:
    """
    typedef enum Status {GOOD, BAD} Status;

    // just returns `GOOD`
    Status hello() {return GOOD;}
    """
    cpdef enum Status:
        GOOD,
        BAD

    Status c_hello "hello"()

def hello():
    return Status(c_hello())

And now:

>>> type(hello())
<enum 'Status'>

Things maybe worth noting:

  • verbatim C-code is used, to make the example self-contained.
  • Using typedef enum X {...} X; to pull enum's type-name from enum's name space into the name space of ordinary variables is a common practice (but obviously this is a matter of taste, so if you prefer STATUS - it is up to you). See the wording in C11-standard on different name spaces here or this great answer (even if it is about struct).
  • cname-trick (i.e. Status c_hello "hello"()) is used, to be able to add a level of indirection and to keep the public interface of the module intact (i.e. cpdef hello()).
  • However, when using hello as cdef-function I would probably use c_hello to avoid overhead of creating an enum - that is the reason hello() is defined as a def-function, so there is no confusion.


标签: enums cython