How to access an enum type by value in cython
Asked Answered
L

3

7

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?

Lakeishalakeland answered 25/4, 2014 at 22:38 Comment(2)
Did you check the answer below?Epigenous
Try this answer for reading cpp files and creating Enums.Dalessio
E
11

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.

Epigenous answered 25/5, 2014 at 18:24 Comment(4)
Note that the OP asks about enum class not enum.Superimpose
The problem I see with this technique is that you cannot convert from a string to the original enum class, and in the pure Python world there is no notion of enum, thus Python cannot easily check the object type, the enum name or the underlying value.Kate
@Kate I haven't checked it, but I think Python recognizes the enum options as integer values. Good point though...Epigenous
That's right, Cython enums are just ints at Python level.Kate
K
5

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!

Kate answered 24/7, 2018 at 14:30 Comment(0)
C
2

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)

Canned answered 4/10, 2021 at 18:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.