我编写了一个小型C ++库来模拟一个ODE系统,并使用Cython包装一个初始化和解决系统的函数。 C ++函数接受一些参数,并返回一个包含两个矩阵的结构:一组用于构造系统的模式,以及系统中已解决的状态每一步。
这些矩阵是使用Eigen库实现的,所以我使用Eigency接口将这些数据结构的底层内容传输到NumPy数组。
如果在Python中我将视图返回到其中一个矩阵,那么一切都很好。但是,我想返回一个包含这两个矩阵的元组,这需要临时存储这个结构,然后返回每个矩阵成员:
## kernel.pyx
from eigency.core cimport *
cdef extern from "kernel_cpp.h":
cdef cppclass network:
MatrixXf state
MatrixXf patterns
cdef network _run "run" (int N, int K, int P, double g, double T, double tau, double dt)
def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
return ndarray_view(_run(N,K,P,g,T,tau,dt).state) # <--- This works
# This does not:
# cdef network net = _run(N,K,P,g,T,tau,dt):
# return (ndarray_view(net.state), ndarray_view(net.patterns))
使用python setup.py build_ext --inplace
构建注释代码时出现的错误是
kernel.cpp:1552:11: error: no matching constructor for initialization of 'network'
network __pyx_v_net;
^`
这对我来说很有意义--Cython正在尝试为网络调用构造函数。但我希望施工能够在&#34;跑步中进行。&#34;这里的正确类型声明是什么?
如果有帮助,代码的精简版本为here。
谢谢!
答案 0 :(得分:3)
值得了解Cython如何转换代码堆栈分配C ++变量的代码:
def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
cdef network net = _run(N,K,P,g,T,tau,dt)
函数内部转换为:
{
// start of function
network net; // actually some mangled variable name (see your error message)
// body of function
net = _run(N,K,P,g,T,tau,dt);
}
即。它需要默认构造函数和复制/移动赋值运算符。这与C ++代码的编写方式不符,因此偶尔会导致问题。 Cython通常假设存在这些(除非你告诉它有关其他构造函数),所以你不需要在cdef cppclass
块中明确地写出这些。
我认为在编译C ++代码时看起来会出现错误消息,并且它没有定义默认构造函数。 (之后的复制赋值应该不是问题,因为C ++会自动生成它)。您有两种选择:
如果您对修改C ++代码感到满意,请定义默认构造函数。这将允许Cython代码按写入方式工作。如果您使用的是&gt; = C ++ 11,那么就可以这么简单:
network() = default;
这可能不会起作用,这取决于内部Eigen类型是否可以默认构建。
如果您不想修改C ++代码,或者事实证明您无法轻松定义默认构造函数,那么您必须修改Cython代码以分配network
与new
。这也意味着你必须自己处理释放:
# earlier
cdef cppclass network:
# need to tell Cython about copy constructor
network(const network&)
# everything else as before
def run(N=10000, K=100, P=30, g=1.5, T=0.5, tau=1e-3, dt=1e-3):
cdef network* net
try:
net = new network(_run(N,K,P,g,T,tau,dt))
return (ndarray_view(net.state), ndarray_view(net.patterns))
finally:
del net
在这种情况下,不需要默认构造函数,只需要复制/移动构造函数(无论如何都必须能够从函数返回network
)。注意使用finally
来确保我们释放内存。