Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access an enum type by value in cython

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?

like image 874
Ben Avatar asked Apr 25 '14 22:04

Ben


3 Answers

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.

like image 76
Saullo G. P. Castro Avatar answered Oct 17 '22 23:10

Saullo G. P. Castro


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:

  1. 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
    
  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!

like image 4
ibarrond Avatar answered Oct 18 '22 00:10

ibarrond


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)

like image 1
Chronial Avatar answered Oct 17 '22 22:10

Chronial