6

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?

hfhc2
  • 4,182
  • 2
  • 27
  • 56
  • This doesn't fix your problem but I think the `typedef enum` is a red-herring - it's useful in C to avoid writing `enum Status`, but there's no reason to do it in Cython really. – DavidW Jan 04 '19 at 23:22

1 Answers1

3

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.
ead
  • 32,758
  • 6
  • 90
  • 153
  • This is why I love stackoverflow so much. Thank you very much for your help :) I ended up wrapping the call like you suggested. – hfhc2 Jan 05 '19 at 12:07