如何通过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++ 对象的函数?