gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 05:04:13 26 4
gpt4 key购买 nike

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

const blob &get_blob();

对于 cython,我使用一个 .pxd 文件来访问 C++ 命名空间:

const blob &get_blob() except +

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

cdef const blob* o = &get_blob()

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

error: assigning to 'const blob *' from incompatible type '__Pyx_FakeReference *'

如果我使用 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;
...
}

关于c++ - 如何连接通过cython返回对对象的引用的c++函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49010508/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com