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

How to convert Python object to C++ type in Cython

本文关键字:转换 C++ 类型 对象 Python Cython 中将      更新时间:2023-10-16

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

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

文件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]'

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

我使用 jupyter 笔记本来简化编译:

%%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 -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++之间的坚实桥梁