如何通过cython接口返回对象引用的c ++函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何通过cython接口返回对象引用的c ++函数相关的知识,希望对你有一定的参考价值。

我正在尝试连接一个返回对象引用的C ++函数:

const blob &get_blob();

对于cython,我使用.pxd文件,它允许我访问C ++命名空间:

const blob &get_blob() except +

然后我在.pyx文件中使用该函数:

cdef const blob* o = &get_blob()

所以我使用地址运算符并赋值给指针变量。但是,在使用cython版本0.23进行编译时出现错误:

错误:从不兼容类型'__Pyx_FakeReference *'分配'const blob *'

如果我用cython版本0.27编译,则不会出现此错误。所以我认为这是旧的cython版本中的一个错误。

基本问题:连接通过cython返回引用的c ++函数的正确方法是什么。我找不到任何相关文档。

答案

你处理引用的方式非常好。

参考文献是Cython的一种步骤子项。我只能推测原因,我试图解释的是:

CPython / Cython是C语言中的家,而不是C ++,C不知道引用,它们大多是指针的语法糖,不能是NULL。 C ++中的引用有一个烦人的属性,你不能做这样的事情:

int &x;
x=some_int_var;

但必须在创建时初始化引用,并且不能再次更改引用:

int &x=some_int_var;

但是,如果你看一下Cython生成的C / CPP代码,你会看到在函数开头声明所有变量的C90模式(因此,可以创建符合C90的C代码) )。为了能够使用引用,为C ++更改它可能意味着很多工作。

所以Cython通过定义一个FakeReference模板类来使用一种解决方法,它包装了一个原始指针:

template<typename T>
class __Pyx_FakeReference {
  public:
    __Pyx_FakeReference() : ptr(NULL) { }
    __Pyx_FakeReference(const T& ref) : ptr(const_cast<T*>(&ref)) { }
    T *operator->() { return ptr; }
    T *operator&() { return ptr; }
    operator T&() { return *ptr; }
    template<typename U> bool operator ==(U other) { return *ptr == other; }
    template<typename U> bool operator !=(U other) { return *ptr != other; }
  private:
    T *ptr;
};

所以对于以下愚蠢的Cython代码:

%%cython --cplus 
from libcpp.vector cimport vector
cdef set_first(vector[int] & vect):
     cdef int * first=&(vect.at(0))
     first[0]=10

我们将获得以下为cdef int * first=&(vect.at(0))行生成的C代码(仅限重要部分):

static PyObject *__pyx_f_5foo_set_first(std::vector<int>  &__pyx_v_vect) {

   # our variable first, not yet initialized (C90 style):
   int *__pyx_v_first; 

   #Fake reference, initialized with NULL  (C90 style):              
   __Pyx_FakeReference<int> __pyx_t_1; 

   #now we use implicit constructor __Pyx_FakeReference(const T& ref), 
   #be aware of const_cast
   #+ (compiler generated, which is Ok because FakeReference 
   #doesn't own the pointer) assignment-operator
   __pyx_t_1 = __pyx_v_vect.at(0);

   #now, cast FakeReference to pointer `first`
   #using operator&
   __pyx_v_first = (&__pyx_t_1);
   ...
 }

有趣且有点奇怪的是,我们可以在函数签名中使用引用,如上面的set_first(vector[int] & vect)

这可能是因为传递的参数不必由Cython处理,并且在cpp-code-level上自动正确处理。

最后但并非最不重要的是,让我们快速检查,一切都按预期工作:

%%cython --cplus 
from libcpp.vector cimport vector
cdef set_first(vector[int] & vect):
     cdef int * first=&(vect.at(0))
     first[0]=10

def create_list(n):
   cdef vector[int] v=range(n)
   set_first(v)
   return v

>>> create_list(2)
[10,1] # yep it worked!

警告:人们可能会尝试尝试以下方法:

%%cython --cplus 
from libcpp.vector cimport vector
cdef set_first(vector[int] & vect):
     first=vect.at(0)
     (&first)[0]=10

希望,first在某种程度上是神奇的(python?)参考。实际上,first的类型为int,最后一行是将此局部变量设置为10的复杂方法,而不是设置向量中第一个元素的方法。以下是与上述版本的重要区别:

static PyObject *__pyx_f_5foo_set_first(std::vector<int>  &__pyx_v_vect) {

   # "int" not "int *"!
   int __pyx_v_first; 


   #now, cast FakeReference  __pyx_t_1 to int via operator() 
   #which return "*ptr" (and not "ptr" as operator&())
   __pyx_v_first = __pyx_t_1;
   ...
 }

以上是关于如何通过cython接口返回对象引用的c ++函数的主要内容,如果未能解决你的问题,请参考以下文章

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

在Java中,如何返回对对象的引用,以便可以使用赋值运算符修改对象

如何在 C++ 中通过引用返回类对象?

如何存储多个数据类型的数组

C 语言Struct 实现运行类型识别 RTTI

C++11 中的左值引用和右值引用的区别