如何在 Cython 中将 Python 对象转换为 C++ 类型

Posted

技术标签:

【中文标题】如何在 Cython 中将 Python 对象转换为 C++ 类型【英文标题】:How to convert Python object to C++ type in Cython 【发布时间】:2017-10-31 21:34:03 【问题描述】:

如何将使用def 定义的 Cython 方法中的 Python 对象参数转换为 C++ 类型?我正在尝试为 C++ 库提供 Cython 包装类,如 Cython 文档的 Using C++ in Cython 部分所述。

这是一个演示我的问题的示例。

文件foo.h

namespace ns 
  class Foo:
    public:
      Foo();
      dosomething(std::shared_ptr<Bar>);

文件bar.h

namespace ns 
  class Bar:
    public:
      Bar();

文件foo.pyx

from libcpp.memory cimport shared_ptr

cdef extern from 'bar.h' namespace 'ns':

    cdef cppclass Bar:
        Bar() except +


cdef extern from 'foo.h' namespace 'ns':

    cdef cppclass Foo:
        Foo() except +
        void dosomething(shared_ptr[Bar])


cdef class PyBar:

    cdef Bar* thisptr

    def __cinit__(self):
        self.thisptr = new Bar()

    def __dealloc__(self):
        del self.thisptr


cdef class PyFoo:

    cdef Foo* thisptr

    def __cinit__(self):
        self.thisptr = new Foo()

    def __dealloc__(self):
        del self.thisptr

    def dosomething(self, bar):
        self.thisptr.dosomething(bar)

文件setup.py

from setuptools import Extension
from setuptools import setup

from Cython.Build import cythonize

extensions = [
    Extension(
        'foo',
        sources=[
            'foo.pyx',
            'foo.cpp',
            'bar.cpp',
        ],
        language='c++',
        include_dirs=['.'],
        extra_compile_args=['-std=c++11'],
    ),
]

setup(
    ext_modules=cythonize(extensions),
)

我尝试编译时出现的错误:

$ python setup.py build_ext
Compiling foo.pyx because it changed.
[1/1] Cythonizing foo.pyx

Error compiling Cython file:
------------------------------------------------------------
...

def __dealloc__(self):
    del self.thisptr

def dosomething(self, bar):
    self.thisptr.dosomething(bar)
                ^
------------------------------------------------------------

foo.pyx:38:33: Cannot convert Python object to 'shared_ptr[Bar]'

【问题讨论】:

Convert Python program to C/C++ code?的可能重复 【参考方案1】:

恐怕我不得不说,foo and bar 真是一个愚蠢的例子,不知道 C++ 代码要完成什么,没有逻辑,也没有实现。但我也根据你的例子编造了这样一个愚蠢的例子,并使它起作用。这很棘手且有问题,希望对您有所帮助。

我使用jupyter notebook来简化编译:

%%file foo.h
#include <memory>

namespace ns 

  class Bar
    public:
      Bar();
  ;

  class Foo
    public:
      Foo(): bar_addr(0) 
      void dosomething(std::shared_ptr<Bar> sp)bar_addr = (size_t)sp.get();
      size_t get_bar_addr()return bar_addr;
    private:
      size_t bar_addr;
  ;

Cython 的部分:

%%cython -f -+ -a
# distutils: include_dirs = .

from libcpp.memory cimport shared_ptr


cdef extern from 'foo.h' namespace 'ns':

    cdef cppclass Foo:
        Foo() except +
        void dosomething(shared_ptr[Bar])
        size_t get_bar_addr()

    cdef cppclass Bar:
        Bar() except +


cdef class PyBar:

    cdef Bar* thisptr

    def __cinit__(self):
        self.thisptr = new Bar()

    def __dealloc__(self):
        del self.thisptr

    def addr(self):
        return <size_t><void*>self.thisptr


cdef class PyFoo:

    cdef Foo* thisptr
    # There is a bug here, see the comments
    cdef PyBar owned_bar  # keep this bar to prevent the underlying pointer losing. 
    cdef shared_ptr[Bar] owned_bar_sp # keep this bar shared_ptr to prevent the underlying pointer from deleting.

    def __cinit__(self):
        self.thisptr = new Foo()

    def __dealloc__(self):
        del self.thisptr

    def dosomething(self, PyBar bar):
        self.owned_bar = bar  
        print("Address passed in: ", self.owned_bar.addr())
        self.owned_bar_sp = shared_ptr[Bar](<Bar*><size_t>self.owned_bar.addr())
        self.thisptr.dosomething(self.owned_bar_sp) 
        print("Address got      : ", self.thisptr.get_bar_addr())

测试:

bar = PyBar()
foo = PyFoo()

foo.dosomething(bar)

关于您的问题“如何在 Cython 中将 Python 对象转换为 C++ 类型 ”,我认为答案是“可以”或“不能”或“可能”,具体取决于上下文。在此特定示例中,“不能”。您必须在 Cython 中进行一些构造或转换,这可以作为一个实体Python 和 C/C++ 之间的桥梁

【讨论】:

我认为不应该这样:1)整个owned_bar业务是不必要的,bar在dsomething中使用时不会被删除。 2) 在调用 dosomething 之后有一个 PyBar.thisptr 的两倍释放:一次由 dealoc 一次通过 shared_ptr. cpp-interface 中的Shared_ptr 会出现一些问题......也许 Bar.thisptr 应该是 shared_ptr[Bar] 类型 @ead 好点!我本能地觉得它有问题。随意编辑它。

以上是关于如何在 Cython 中将 Python 对象转换为 C++ 类型的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Cython 中将大型 malloc 数组返回或保存为 Python 对象?

如何在 Windows 8.1 中的 anaconda(python3.6) 中将 cython pyx 构建为 pyd?

Cython 和 SIMD 内在函数:防止 SIMD 内在函数的参数转换为 python 对象

使用cython将double转换为char *

如何在 Python 中将类对象转换为 JSON

如何使用 Cython 向 Python 公开返回 C++ 对象的函数?