16

I am trying to wrap an enum class in a c++ header file for use in a cython project.

For example, how can this

enum class Color {red, green = 20, blue};

be wrapped with Cython.

8
  • 1
    Can you elaborate with an example having cdef enum Foo: [...] does not answer your needs ? Commented Jun 23, 2015 at 11:54
  • 1
    I am not wrapping a c enum, but a c++ enum class so this doesn't work Commented Jun 23, 2015 at 11:57
  • Do the enum as a ctypedef and the contents of the enum in a namespace? (I haven't tested this, but it seems it might work) Commented Jun 24, 2015 at 6:24
  • David can you elaborate please? Commented Jun 24, 2015 at 13:17
  • I'm thinking ctypedef int enum_type or possible cdef cppclass enum_type: pass to define the type. Then to define the members doing cdef extern from "somefile.hpp" namespace "enum_type": cdef enum_type BLUE (etc). I think something like that should generate the right c++ code (i.e. BLUE will be replaced with enum_type::BLUE) Commented Jun 24, 2015 at 13:45

4 Answers 4

14

CPP class

enum class Color {red, green = 20, blue}; 

Definition of type

cdef extern from "colors.h":
  cdef cppclass Color:
    pass

Definition of color types

cdef extern from "colors.h" namespace "Color":
  cdef Color red
  cdef Color green
  cdef Color blue

Python implementation

cdef class PyColor:
  cdef Color thisobj
  def __cinit__(self, int val):
    self.thisobj = <Color> val

  def get_color_type(self):
    cdef c = {<int>red : "red", <int> green : "green", <int> blue : "blue"}
    return c[<int>self.thisobj]
Sign up to request clarification or add additional context in comments.

1 Comment

With Cython version >= 3.0 consider this answer: stackoverflow.com/a/67138945/5769463
11

The latest cython (3.x) has a direct support for c++ enum class, it's documented here: https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#scoped-enumerations

Here is an example:

// cpp header
enum class State: int
{
    Good,
    Bad,
    Unknown,
};


const char* foo(State s){
    switch (s){
        case State::Good:
            return "Good";
        case State::Bad:
            return "Bad";
        case State::Unknown:
            return "Unknown";
    }
}

Cython's side

cdef extern from "test.h":
    cpdef enum class State(int):
        Good,
        Bad,
        Unknown,

    const char* foo(State s)
    
def py_foo(State s):
    return foo(s)

call py_foo(State.Good) returns b'Good'

1 Comment

Pretty cool. I asked this question 6 years ago, and it is nice to finally be able to accept a real native answer, though thank you to all of the other answers that provided decent workarounds
5

Another alternative that allows using PEP-435 Enums as mentioned in Cython docs is as follows:

foo.h

namespace foo {
enum class Bar : uint32_t {
    Zero = 0,
    One = 1
};
}

foo.pxd

from libc.stdint cimport uint32_t

cdef extern from "foo.h" namespace 'foo':

    cdef enum _Bar 'foo::Bar':
        _Zero 'foo::Bar::Zero'
        _One  'foo::Bar::One'


cpdef enum Bar:
    Zero = <uint32_t> _Zero
    One  = <uint32_t> _One

main.pyx

from foo cimport Bar

print(Bar.Zero)
print(Bar.One)

# or iterate over elements
for value in Bar:
    print(value)

Comments

4

Here's an alternative solution that uses the ability to change the name of cython and C++ identifiers.

header.hpp

namespace foo {
enum class Bar : uint32_t {
    BAZ,
    QUUX
};
}

header.pxd

cdef extern from "header.hpp" namespace "foo::Bar":
    cdef enum Bar "foo::Bar":
        BAZ,
        QUUX

main.pyx

from header cimport *
cdef void doit(Bar b):
    pass

doit(BAZ) # Not Bar.BAZ, which would have been nicer.

It's effectively telling cython that there exists a namespace called "foo::Bar", and puts a C-style enum in it. To counteract the fact that Bar would otherwise become "foo::Bar::Bar" that is also given an overridden name. It does mean that Bar::BAZ is referred to as BAZ in cython, rather than Bar.BAZ which would be a more idiomatic representation of enum classes, but it seems close enough.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.