繁体   English   中英

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

[英]How to interface a c++ function that returns a reference to an object via cython

我正在尝试连接一个返回对象引用的 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 类来使用一种解决方法,该类包装了一个原始指针:

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 代码级别自动正确处理。

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

%%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?)参考。 实际上, firstint类型,最后一行是将此局部变量设置为10的复杂方法,而不是设置 vector 中第一个元素的方法。 以下是与上述版本的主要区别:

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;
   ...
 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM