简体   繁体   English

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

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

I'm trying to interface a C++ function that returns a reference to an object:我正在尝试连接一个返回对象引用的 C++ 函数:

const blob &get_blob();

For cython, i use a .pxd file that gives me access to the C++ namespace:对于 cython,我使用了一个.pxd文件,它可以让我访问 C++ 命名空间:

const blob &get_blob() except +

I then use the function in a .pyx file:然后我在.pyx文件中使用该函数:

cdef const blob* o = &get_blob()

So i use the address operator and assign to a pointer variable.所以我使用地址运算符并分配给一个指针变量。 However, while compiling with cython version 0.23 I get an error:但是,在使用 cython 0.23 版进行编译时,出现错误:

error: assigning to 'const blob *' from incompatible type '__Pyx_FakeReference *'错误:从不兼容的类型“__Pyx_FakeReference *”分配给“const blob *”

This error does not appear if i compile with cython version 0.27.如果我使用 cython 0.27 版编译,则不会出现此错误。 So I consider this a bug in the older cython version.所以我认为这是旧 cython 版本中的一个错误。

The essential question: What is the proper way of interfacing a c++ function that returns a reference via cython.基本问题:连接通过 cython 返回引用的 c++ 函数的正确方法是什么。 I couldn't find any documentation on that.我找不到任何关于此的文档。

Your way of handling references is pretty good.你处理引用的方式非常好。

References are a kind of step-children for Cython.引用是 Cython 的一种继子。 I can only speculate about reasons, my try to explain would be:我只能推测原因,我试图解释的是:

CPython/Cython are home in C and not C++, and C doesn't know references, which are mostly a syntactic sugar for pointers which cannot be NULL . CPython/Cython 是 C 而不是 C++ 的家,C 不知道引用,这主要是指针不能为NULL的语法糖。 The references in C++ have an annoying property, you cannot do something like this: C++ 中的引用有一个恼人的属性,你不能做这样的事情:

int &x;
x=some_int_var;

but have to initialize the reference at it's creation and cannot change the reference ever again:但必须在创建时初始化引用,并且不能再次更改引用:

int &x=some_int_var;

However, if you take a look into the C/CPP-code produced by Cython, you will see the C90-pattern of declaring all variables at the beginning of a function (thus, it is possible to create a C90-compliant C-code).但是,如果您查看 Cython 生成的 C/CPP 代码,您将看到在函数开头声明所有变量的 C90 模式(因此,可以创建符合 C90 的 C 代码)。 It would probably mean a lot of work to change this for C++ in order to be able to use references.为了能够使用引用,为 C++ 更改这一点可能意味着很多工作。

So Cython uses a workaround by defining a FakeReference-template class, which wraps a raw-pointer:因此,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;
};

So for the following silly Cython-code:因此,对于以下愚蠢的 Cython 代码:

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

we would get the following generated C-code for the line cdef int * first=&(vect.at(0)) (only important parts):我们将获得以下为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);
   ...
 }

The funny and somewhat strange thing is that we can use references in function-signatures, like above set_first(vector[int] & vect) .有趣且有些奇怪的事情是我们可以在函数签名中使用引用,就像上面的set_first(vector[int] & vect)

This is probably due to the fact, that the passed arguments don't have to be handled by the Cython and are automatically handled correctly on the cpp-code-level.这可能是因为传递的参数不必由 Cython 处理,而是在 cpp 代码级别自动正确处理。

Last but not least, let's do a fast check, that everything works as expected:最后但并非最不重要的一点是,让我们快速检查一下,一切都按预期进行:

%%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!

A warning: one might be tempted to try something like:警告:人们可能会尝试尝试以下操作:

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

in hope, that first is somehow magically a (python?) reference.希望, first在某种程度上神奇地是一个(python?)参考。 In reality, first is of type int and the last line is a complicated way to set this local variable to 10 and not a way to set the first element in vector.实际上, firstint类型,最后一行是将此局部变量设置为10的复杂方法,而不是设置 vector 中第一个元素的方法。 Here are the crucial differences to the version above:以下是与上述版本的主要区别:

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