如何在Cython中从另一个包装对象返回包装的c++对象

How do I return wrapped C++ Objects in Cython from another wrapped Object?

本文关键字:对象 包装 返回 c++ 另一个 Cython      更新时间:2023-10-16

我一直在用Cython包装c++类。现在我有以下问题:类A有一个方法foo(),它返回类b的实例。两者都是c++类。两者都在Cython中包装为PyA和PyB类。我想让方法foo()以用户调用PyA_object.foo()并获得PyB_object的方式对用户进行访问。我如何实现这个方法?

cimport cppClassA
cdef class A:
    cdef cppClassA.A *ptr
    ...
def foo():
    return self.ptr.foo()

这样我直接返回c++类B,但我想返回一个PyB。因此,我需要创建一个PyB对象,但是我如何构造PyB的cinit()方法?我怎么能给返回的对象从self.ptr.foo()到一个新的PyB对象,并返回它?

从您的问题中不完全清楚foo是否通过指针或值返回(即是否具有签名B* foo()B foo())。你需要做什么取决于这个——我已经展示了两者。

我认为你遇到的问题是,PyB__init____cinit__方法只能接受Python对象作为参数,所以你没有办法传递c++ B对象。解决方案是创建一个PyB,它要么为空,要么将默认的c++对象作为ptr,然后替换它。

c++代码的包装是这样的

cdef extern from "somefile.hpp":
    cdef cppclass A:
        B foo1()
        B* foo2()
    cdef cppclass B:
        # only need to provide these for foo1_alternative
        B()
        B(const B&)

请注意,我提供了两个版本的foo - foo1返回值和foo2返回指针。

Python B holder (PyB)看起来像

cdef class PyB:
    cdef B* ptr
    def __cinit__(self,make_ptr=True):
        if make_ptr:
            self.ptr = new B()
        else:
            self.ptr = NULL
    def __dealloc__(self):
        del self.ptr

我提供了一个可选的make_ptr参数,您可以将其设置为False,以避免将self.ptr设置为任何值。

Python A holder (PyA)则为

cdef class PyA:
    cdef A* ptr
    # code goes here
    def foo1(self):
        cdef PyB val = PyB()
        val.ptr[0] = self.ptr.foo1() # use move (or copy) assignment operator
    def foo1_alternative(self):
        cdef PyB val = PyB(make_ptr=False)
        val.ptr = new B(self.ptr.foo1()) # use move (or copy) constructor
    def foo2(self):
        cdef PyB val = PyB(make_ptr=False)
        val.ptr = self.ptr.foo2()

对于返回指针的foo2,您只需创建一个空的PyB并将返回的Foo*赋值给self.ptr。对于foo1,您有两个选择,其中一个涉及到创建一个PyB,然后使用移动(或复制)赋值操作符来复制数据。第二个选项涉及创建一个空的PyB,然后使用B的移动(或复制)构造函数来创建一个B*

如果可以的话,你最好使用move构造函数而不是copy构造函数,但是你不需要告诉Cython这两种构造函数(c++会自动选择正确的构造函数)。