[英]How to interface a function that returns a C++ custom class with Cython?
[英]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?)参考。 实际上, first
是int
类型,最后一行是将此局部变量设置为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.