Завершение enum класса для Cython

Я пытаюсь обернуть класс enum в заголовочный файл C++ для использования в проекте Cython. Я гуглил и не могу узнать, как этого добиться - поддерживается ли он?

4 ответа

Решение

Класс CPP

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

Определение типа

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

Определение типов цвета

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

Реализация Python

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]

Последний cython (3.x) имеет прямую поддержку c ++ enum class, это задокументировано здесь:https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#scoped-enumerations

Вот пример:

      // 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";
    }
}

Сторона Сайтона

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

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

вызов py_foo(State.Good) возвращается b'Good'

Другая альтернатива, которая позволяет использовать перечисления PEP-435, как указано в документации Cython, выглядит следующим образом:

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)

Вот альтернативное решение, которое использует возможность изменения имени идентификаторов Cython и C++.

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.

Это эффективно говорит Cython, что существует пространство имен с именем "foo::Bar", и помещает в него перечисление в стиле C. Чтобы противодействовать тому факту, что в противном случае Bar станет "foo::Bar::Bar", ему также присваивается переопределенное имя. Это означает, что Bar::BAZ упоминается как BAZ в Cython, а не Bar.BAZ, который был бы более идиоматическим представлением перечислимых классов, но это кажется достаточно близким.

Другие вопросы по тегам