I was handed some code that looks like this:
In "header.hpp":
enum class my_enum_type {
val1 = 0;
...
}
In "header_lib.pyx":
cdef extern from "header.hpp":
enum my_enum_type:
val1 = 0;
...
...
Later in "header_lib.pyx":
def foo():
...
return my_enum_type.val1
I was told this should have no problems, but that is not the case from my experience just now,and as evident in this post: Defining enums in Cython code that will be used in the C part of code.
However, it doesn't recognize "val1" by itself either if I write "return val1". What's the correct way to do this?
You can declare a enum
in Cython as:
ctypedef enum options: OPT1, OPT2, OPT3
or
ctypedef enum options:
OPT1,
OPT2,
OPT3
And and example could be:
def main():
cdef options test
test = OPT2
f(test)
cdef void f(options inp):
if inp == OPT1:
print('OPT1')
elif inp == OPT2:
print('OPT2')
elif inp == OPT3:
print('OPT3')
When running main()
you will see "OPT2"
printed. You could pass the variable test
to a C
or C++
function in the same way as shown here for the cdef
function.
To maintain the duality of Cython/C/C++ enum class and Python Enum class functionalities, I propose a solution with mirroring classes. This solution is divided in two components:
On one side we have a Cython/C/C++ enum. This enum can either be originated by wrapping C/C++ code in an extern block...
cdef extern from "header.h":
enum my_c_enum_type:
OPT1 = 0,
OPT2,
OPT3,
... or directly in Cython.
ctypedef enum my_cy_enum_type:
OPT1=0, #Default value is 0
OPT2, #Default value is 1
OPT3, #Default value is 2
On the other we have Python. Since Python 3.4, there is a supported class Enum that emulates that functionality (link to official documentation). Here we should replicate the enum elements in pure Python. You can make use of the auto
function to fill the Enum just like in C/C++/Cython:
from enum import Enum, auto
class my_py_enum_type(Enum):
OPT1=0
OPT2=auto()
OPT3=auto()
Now, in order to use this dual Python & Cython solution:
# PYTHON: we use the my_py_enum_type Enum
enum_obj = my_py_enum_type(1) # Corresponds to OPT2
print(enum_obj.value, enum_obj.name) # We can access the value and the name
#> 1, OPT2
s='OPT2'; enum_obj = getattr(my_py_enum_type, s) # We can create them from the string 'OPT2'
# PYTHON TO CYTHON
def func(enum_obj):
if not isinstance(enum_obj, my_py_enum_type):
raise TypeError
# Use in a cython function that accepts only my_c_enum_type Cython enum
myCythonEnumFunc(enum_obj.value) # Convert to C/Cython enum using value
...
# CYTHON TO PYTHON
def func():
# a Cython enum
cdef my_c_enum_type cy_enum_obj = val1
# creating a python enum
return my_p_enum_type(cy_enum_obj) # Convert using the full cy_enum
...
This solution translates Cython/C enums (which in Python are mere ints) into a proper Python Enum class with the same characteristics, and translates the value inside a Python Enum object into the original Cython class. You get the best of both worlds!
You can use cpdef to directly create a Python Enum in cython:
cpdef enum my_enum_type:
val1 = 0
...
This only gets a very short mention in the docs (search for cpdef enum
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With