【问题标题】:Passing C++ pointer as argument into Cython function将 C++ 指针作为参数传递给 Cython 函数
【发布时间】:2015-09-27 20:08:41
【问题描述】:
cdef extern from "Foo.h":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

    def __cinit__(self, Bar *b)
        bar = b

这总是会给我类似的信息:
Cannot convert Python object argument to type 'Bar *'

有没有办法做到这一点,还是我需要从 Bar 对象中提取所有内容,创建一个 Python 等效项,将其传入,然后在 PyClass 中重建它?

【问题讨论】:

    标签: python cython


    【解决方案1】:

    我在尝试将带有结构的 C 代码包装为 python 类时遇到了这个问题。问题似乎是包括__init____cinit__ 在内的“特殊”函数必须声明为def 而不是cdef。这意味着它们可以从普通的python中调用,因此类型参数被有效地忽略了,一切都被视为对象。

    在 J.F. Sebastian 的回答中,修复不是包装 - 双精度是基本的数字类型,因此 C/C++ 类型和 Python 对象之间存在默认转换。 Czarek 的回答基本上是正确的——你需要使用一个假的构造函数,使用一个全局函数。不能使用 @staticmethod 装饰器,因为它们不能应用于 cdef 函数。在提供的原始示例中,答案看起来更简单。

    cdef extern from "Foo.h":
        cdef cppclass Bar:
            pass
    
    cdef class PyClass:
        cdef Bar *bar
    
    cdef PyClass_Init(Bar *b):
        result = PyClass()
        result.bar = b
        return result
    

    【讨论】:

    • 在最新版本的 Cython 上(至少从 0.22 开始),@staticmethod 装饰器可以应用于 cdef 函数,因此现在可以将全局创建器函数变成一个静态类,以实现更整洁的组织.
    • Barc++类不是基本数值类型,没有默认转换。
    • @J.F.Sebastian 你能解释一下你的意思吗? Bar 不必是基本的数字类型才能存储指向它的指针。
    • @Amoss:我的答案是Bar。您说:“在 J.F. Sebastian 的回答中,解决方法不是包装 - 双精度型是基本的数字类型,因此 C/C++ 类型和 Python 对象之间存在默认转换。” 我的回答是关于包装的。正如您所说:您不能将 C++ 指针传递给 def 函数。
    • 在您的解决方案中,包装是不必要的,并且您已经在具有默认提升的参数上演示了它。添加fake构造函数就足以解决问题,无需包装c++类。
    【解决方案2】:

    Cython 0.21 开始,可以使用@staticmethod 装饰器声明cdef 方法。这允许使用非 Python 参数的静态创建者方法:

    cdef extern from "Foo.h":
        cdef cppclass Bar:
            pass
    
    cdef class PyClass:
        cdef Bar *bar
    
        @staticmethod
        cdef create(Bar *bar):
            cdef PyClass pc = PyClass()
            pc.bar = bar
            return pc
    

    【讨论】:

      【解决方案3】:

      为每个 cdef 类创建一个充当构造函数的全局 cdef 函数,CefResponse 是一个 C++ 对象,PyResponse 是一个与 c++ 对象等效的 python:

      cdef object CreatePyResponse(CefRefPtr[CefResponse] cefResponse):
      
          pyResponse = PyResponse()
          pyResponse.cefResponse = cefResponse
          return pyResponse
      
      cdef class PyResponse:
      
          cdef CefRefPtr[CefResponse] cefResponse
      
          def GetStatus(self):
      
              return (<CefResponse*>(self.cefResponse.get())).GetStatus()
      

      所以不要调用resp = PyResponse(cppObject),而是调用resp = CreatePyResponse(cppObject)

      取自 CEF Python 的示例: https://code.google.com/p/cefpython/source/browse/cefpython/response.pyx?r=0250b65e046a

      【讨论】:

        【解决方案4】:

        Python 类接受 Python 参数。要传递 C++ 参数,您需要包装它:

        # distutils: language = c++
        
        cdef extern from "Foo.h" namespace "baz":
            cdef cppclass Bar:
                 Bar(double d)
                 double get()
        
        cdef class PyBar: # wrap Bar class
            cdef Bar *thisptr
            def __cinit__(self, double d):
                self.thisptr = new Bar(d)
            def __dealloc__(self):
                del self.thisptr
            property d:
                def __get__(self):
                    return self.thisptr.get()
        

        PyBar 实例可以用作 Cython 和纯 Python 中的任何其他 Python 对象:

        class PyClass:
            def __init__(self, PyBar bar):
                self.bar = bar
        
        print(PyClass(PyBar(1)).bar.d)
        

        【讨论】:

        • @ChrisArndt:您是否声称 PyClass 接受 bar 的值?
        • 不,我是说类构造函数不采用 C 指针,这是 OP 想要实现的。顺便说一句,手册中有一个部分(可能是在提出这个问题后添加的)解决了这个特定问题:cython.readthedocs.io/en/latest/src/userguide/… 这是如何实现的:github.com/SpotlightKid/jack_mixer/blob/…
        • @ChrisArndt:你明白你不能将 C 指针传递给 def 方法而不包装它吗?
        猜你喜欢
        • 1970-01-01
        • 2012-11-23
        • 1970-01-01
        • 2018-05-07
        • 2015-11-22
        • 1970-01-01
        • 1970-01-01
        • 2013-01-25
        • 2019-08-17
        相关资源
        最近更新 更多