将大型复杂数组从Python传递到C ++-我最好的选择是什么?

将大型复杂数组从Python传递到C ++-我最好的选择是什么?,第1张

将大型复杂数组从Python传递到C ++-我最好的选择是什么?

我多次使用的简单解决方案是将您的“ C ++端”构建为dll(= Linux / OS X上的共享对象),提供一个简单的, 类似于C的
入口点(直整数,指针和连字符,没有STL内容) )并将数据通过

ctypes

这避免了boost / SIP / Swig /
…构建梦night,可以保持零拷贝(使用ctypes可以将直指针指向numpy数据),并允许您做任何您想做的事情(尤其是在构建方面-
没有friggin的distutils,没有提升,什么也没有-用可以在C 方面构建类似于C的dll的方式进行构建。让您的C
算法可以从其他语言调用(这几乎是任何一种语言都可以与C库进行交互的方式),这也具有很好的副作用。


这是一个快速的人工示例。C ++方面只是:

extern "C" {double sum_it(double *array, int size) {    double ret = 0.;    for(int i=0; i<size; ++i) {        ret += array[i];    }    return ret;}}

必须将其编译为dll(在Windows上)或

.so
(在Linux上),以确保导出
sum_it
功能(使用gcc自动运行,需要
.def
使用VC
++的文件)。

在Python方面,我们可以使用类似

import ctypesimport osimport sysimport numpy as nppath = os.path.dirname(__file__)cdll = ctypes.CDLL(os.path.join(path, "summer.dll" if sys.platform.startswith("win") else "summer.so"))_sum_it = cdll.sum_it_sum_it.restype = ctypes.c_doubledef sum_it(l):    if isinstance(l, np.ndarray) and l.dtype == np.float64 and len(l.shape)==1:        # it's already a numpy array with the right features - go zero-copy        a = l.ctypes.data    else:        # it's a list or something else - try to create a copy        arr_t = ctypes.c_double * len(l)        a = arr_t(*l)    return _sum_it(a, len(l))

确保数据正确封送;然后调用该函数就很简单

import summerimport numpy as np# from a list (with copy)print summer.sum_it([1, 2, 3, 4.5])# from a numpy array of the right type - zero-copyprint summer.sum_it(np.array([3., 4., 5.]))

请参阅

ctypes
文档以获取有关如何使用它的更多信息。另请参阅numpy中的相关文档。


对于复数,情况要稍微复杂一些,因为ctypes中没有内置函数。如果我们想

std::complex<double>
在C
端使用(可以保证可以很好地与numpy复杂布局配合使用,即两个双精度序列),则可以将C
端写为:

extern "C" {std::complex<double> sum_it_cplx(std::complex<double> *array, int size) {    std::complex<double> ret(0., 0.);    for(int i=0; i<size; ++i) {        ret += array[i];    }    return ret;}}

然后,在Python端,我们必须复制

c_complex
布局以获取返回值(或能够构建没有numpy的复杂数组):

class c_complex(ctypes.Structure):    # Complex number, compatible with std::complex layout    _fields_ = [("real", ctypes.c_double), ("imag", ctypes.c_double)]    def __init__(self, pycomplex):        # Init from Python complex        self.real = pycomplex.real        self.imag = pycomplex.imag    def to_complex(self):        # Convert to Python complex        return self.real + (1.j) * self.imag

继承自

ctypes.Structure
启用ctypes编组魔术,这是根据
_fields_
成员执行的;构造函数和其他方法只是为了在Python方面易于使用。

然后,我们必须告诉ctypes返回类型

_sum_it_cplx = cdll.sum_it_cplx_sum_it_cplx.restype = c_complex

最后以与上一个类似的方式编写我们的包装器:

def sum_it_cplx(l):    if isinstance(l, np.ndarray) and l.dtype == np.complex and len(l.shape)==1:        # the numpy array layout for complexes (sequence of two double) is already        # compatible with std::complex (see https://stackoverflow.com/a/5020268/214671)        a = l.ctypes.data    else:        # otherwise, try to build our c_complex        arr_t = c_complex * len(l)        a = arr_t(*(c_complex(r) for r in l))    ret = _sum_it_cplx(a, len(l))    return ret.to_complex()

如上测试

# from a complex list (with copy)print summer.sum_it_cplx([1. + 0.j, 0 + 1.j, 2 + 2.j])# from a numpy array of the right type - zero-copyprint summer.sum_it_cplx(np.array([1. + 0.j, 0 + 1.j, 2 + 2.j]))

产生预期的结果:

(3+3j)(3+3j)


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5617715.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存