使用Cython包装枚举类

15

我想在一个C++头文件中封装一个枚举类,以供在cython项目中使用。

例如,如何实现以下代码:

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

使用Cython进行封装。


1
你能举个例子详细说明 cdef enum Foo: [...] 不能满足你的需求吗? - coincoin
1
我不是在封装C枚举,而是在封装C++枚举类,所以这样做行不通。 - user3684792
大卫,你能详细说明一下吗? - user3684792
你最终解决了这个问题吗? - AdmiralJonB
是的,但我进行了黑客攻击。我想要的用例是访问返回枚举类的类的成员函数。我不得不更改目标库中的此成员函数,以返回“枚举类包装”。这基本上是一个新类,它只有一个枚举类作为其成员,并具有适当的访问器来获取值。我知道这很丑陋... - user3684792
显示剩余3条评论
4个回答

14

C++类

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]

2
如果您使用的是Cython版本>= 3.0,请考虑查看此答案:https://dev59.com/C10Z5IYBdhLWcg3w3DQW#67138945 - ead

10

最新版本的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";
    }
}
Cython 的一面。
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)

调用 py_foo(State.Good) 返回 b'Good'


很酷。我6年前问了这个问题,现在终于能够接受一个真正的本地答案,虽然感谢所有提供不错解决方法的其他答案。 - user3684792

5

另一种选择是按照Cython文档中提到的方式,使用PEP-435枚举:

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)

4
这里有一种替代方案,可以利用更改cython和C++标识符名称的能力来实现。
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在cython中被称为BAZ,而不是Bar.BAZ,后者是enum class更符合惯用表示法的方式,但看起来已经足够接近了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接