在 Cython 中包装返回复杂类型向量的函数
Posted
技术标签:
【中文标题】在 Cython 中包装返回复杂类型向量的函数【英文标题】:Wrapping a function returning a Vector of Complex types in Cython 【发布时间】:2019-09-16 11:44:10 【问题描述】:我目前正在尝试包装我创建的 C++ 类。其中一个函数返回一个包含复数浮点数的向量:
std::vector<std::vector<std::complex<float>>>
我的 DataBridge.pxd 文件如下所示:
# distutils: language = c++
from libcpp cimport bool
from libcpp cimport float
from libcpp.vector cimport vector
from libcpp.complex cimport complex
from libc.float cimport float
cimport numpy as np
import numpy as np
cdef extern from "projectxcpp/bridge.cpp":
pass
# Declare the class with cdef
cdef extern from "projectxcpp/bridge.hpp":
cdef cppclass DataBridge:
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()
我的 bridge.hpp 文件如下所示:
#ifndef BRIDGE_HPP
#define BRIDGE_HPP
#include <vector>
#include <complex>
...
class DataBridge
public:
int start(void);
int stop(void);
bool isDataReady(void);
std::vector<std::vector<std::complex<float>>> getData(void);
private:
...
;
#endif
然后,尝试运行 setup.py,给出以下消息:
[1/1] Cythonizing snowconecpp.pyx
Error compiling Cython file:
------------------------------------------------------------
...
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()
^
------------------------------------------------------------
DataBridge.pxd:24:29: Array size must be a compile time constant
Error compiling Cython file:
------------------------------------------------------------
...
DataBridge() except +
void start()
void stop()
bool isDataReady()
vector[vector[complex[float]]] getData()
^
------------------------------------------------------------
DataBridge.pxd:24:29: unknown type in template argument
Traceback (most recent call last):
File "setup.py", line 15, in <module>
setup(ext_modules=cythonize("projectxcpp.pyx", language_level="3"))
File ".../lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1096, in cythonize
cythonize_one(*args)
File ".../lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1219, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: projectxcpp.pyx
我的 setup.py 文件只包含:
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("projectxcpp.pyx", language_level="3"))
当在我的包装器中将类型声明为 complex[float] e
或 vector[vector[float]] e
时,这些将解析并移至编译的下一步。
只是想知道在 Cython 的向量中放置复杂类型是否有特殊要求 - 似乎无法为涉及 C++ 包装的这种或类似情况找到答案。
我的最终目标是能够将这些数据读入一个 numpy 数组。如果有更好的解决方法,请让我也知道 - 谢谢!
【问题讨论】:
【参考方案1】:我认为问题在于 complex
已经是 Python/Cython 中的一种类型(并且由于与 C 或 C++ 复杂的重叠......)。如果你重命名它,或者通过它的完整路径来解决它,它就可以工作
from libcpp.complex cimport complex as cpp_complex
# later
vector[vector[cpp_complex[float]]]
或
cimport libcpp.complex
# later
vector[vector[libcpp.complex.complex[float]]]
对于问题的更一般的部分:我的偏好是不为这样的 Numpy 数组提供数据。部分原因是我真的不喜欢将 2D 数组定义为指针到指针或向量的向量。您最好使用与形状信息一起存储的一维数组/向量,以便您对其进行索引。
有几个与 Numpy 交互的选项:
如果您事先知道大小,则使用 Numpy 分配内存并将该内存传递给 C++(您可以获得指向 Numpy 数组的第一个元素的指针)。这样 C++ 代码会修改 Numpy 的内存,而您不必复制数据。
如果你想用 C++ 分配内存,那么你可以用 implements the buffer protocol 的 Cython 类来包装它。内存由 C++ 管理,但 Numpy 可以直接访问内存而无需复制。 (您可能需要实现移动构造函数以避免复制,但这相当简单)。
您可以使用 Numpy C API 函数 PyArray_SimpleNewFromData
让 Numpy 包装内存。这需要小心确保它在正确的时间被释放。
您可以做您当前正在做的事情并复制数据。这实现起来非常简单,但每次您想将数据从 C++ 移动到 Python 时都会添加一个复制操作。
【讨论】:
以上是关于在 Cython 中包装返回复杂类型向量的函数的主要内容,如果未能解决你的问题,请参考以下文章
在 VStack 中包装 WebView 时出错:无法推断复杂的闭包返回类型;添加显式类型以消除歧义
Cython - 将 C++ 函数返回的 C++(向量和非向量)对象暴露给 Python