如何公开返回C++;在不复制对象的情况下将对象复制到Python? 我学会了如何通过复制对象来公开一个C++对象返回到Python的函数。必须进行复制似乎不是最佳选择。如何在不复制对象的情况下返回该对象?i、 e.如何直接访问PyPeakDetection.getPeaks(在peak\u detection.pyx中定义)中的self.thisptr.getPeaks(数据)返回的峰值
峰值检测.hpp 峰值检测 <代码> diditul:语言= C++ #distutils:sources=peak_detection.cpp 从libcpp.vector cimport vector 从libcpp.map cimport映射 从libcpp.string到cimport string “峰值水电站”的cdef外部: cdef类峰值: 峰值() 峰值(峰值和峰值) 浮动频率 cdef类PyPeak: cdef峰值*此PTR 定义(自我): self.thisptr=新峰值() def uu dealoc uu(自我): del self.thisptr cdef副本(自身、峰值和其他): del self.thisptr self.thisptr=新峰值(其他) 定义报告(自我): 返回“”格式(self.freq、self.mag) 属性频率: def\uu get\uu(self):返回self.thisptr.freq 定义设置(self,freq):self.thisptr.freq=freq 物业杂志: def\uu get\uu(self):返回self.thisptr.mag def _u设置(自,磁):self.thisptr.mag=mag 来自“peak_detection.hpp”的cdef外部: cdef CPPCClass峰值检测: PeakDetection(映射[字符串,字符串]) 向量[Peak]获取峰值(向量[float]) cdef类PyPeakDetection: cdef峰值检测*此PTR def ___; cinit(self,map[string,string]config): self.thisptr=新峰值检测(配置) def uu dealoc uu(自我): del self.thisptr def getPeaks(自身、数据): cdef峰值 cdef PyPeak新峰 cdef向量[Peak]peaks=self.thisptr.getPeaks(数据) retval=[] 对于峰值中的峰值: 新峰值=PyPeak() 新的_peak.复制(peak)#我如何避免复制? retval.append(新峰值) 返回返回如何公开返回C++;在不复制对象的情况下将对象复制到Python? 我学会了如何通过复制对象来公开一个C++对象返回到Python的函数。必须进行复制似乎不是最佳选择。如何在不复制对象的情况下返回该对象?i、 e.如何直接访问PyPeakDetection.getPeaks(在peak\u detection.pyx中定义)中的self.thisptr.getPeaks(数据)返回的峰值,python,c++,cython,Python,C++,Cython,峰值检测.hpp 峰值检测 diditul:语言= C++ #distutils:sources=peak_detection.cpp 从libcpp.vector cimport vector 从libcpp.map cimport映射 从libcpp.string到cimport string “峰值水电站”的cdef外部: cdef类峰值: 峰值() 峰值(峰值和峰值) 浮动频率 cdef类PyPeak: cdef峰值*此PTR 定义(自我): self.thisptr=新峰值() de
>P>有两个项目,它们完成了C++代码与Python的接口,它们经受住了时间和时间的考验。这两种方法都是通过向相关的C/C++代码添加额外的标记,并生成动态加载的python扩展库(.so文件)和相关的python模块来实现的
但是,根据您的用例,可能还有一些附加标记看起来像“复制”。但是,复制不应该是广泛的,它将全部暴露在C++代码中,而不是在Cython/PyRx中逐字地复制。
< P>如果您有一个现代C++编译器,可以使用rValk引用,move构造函数和std::move它非常简单。我认为最简单的方法是为向量创建Cython包装器,然后使用move构造函数来保存向量的内容 显示的所有代码都在peak_detection_ux.pyx中 第一次换行std::move
。为了简单起见,我只包装了我们想要的一个案例(vector
),而不是在模板上乱搞
cdef extern from "<utility>":
vector[Peak]&& move(vector[Peak]&&) # just define for peak rather than anything else
然后定义类,将峰值
包裹起来。这与其他类略有不同,因为它不拥有它所包装的Peak
(向量拥有)。否则,大多数函数保持不变
cdef class PyPeak2:
cdef int idx
cdef PyPeakVector vector # keep this alive, since it owns the peak rather that PyPeak2
def __cinit__(self,PyPeakVector vec,idx):
self.vector = vec
self.idx = idx
cdef Peak* getthisptr(self):
# lookup the pointer each time - it isn't generally safe
# to store pointers incase the vector is resized
return &self.vector.vec[self.idx]
# rest of functions as is
# don't define a destructor since we don't own the Peak
最后,实现getPeaks()
替代方法:
如果Peak
不重要,那么在构建PyPeak
时,您可以选择在Peak
上调用move
而不是在向量上调用move
的方法。在这里的情况下,移动和复制将等同于'Peak'
如果不能使用C++11功能,则需要稍微更改接口。而不是拥有C++ <代码> GETPASS 函数返回一个向量,它将一个空向量引用(由<代码> PyPeakVector < /COD>)作为输入参数并写入其中。包装的其余部分大部分保持不变。您可以将不希望复制的对象包装到另一个具有值语义但不复制其保留值的对象中,如shared_ptr。不确定这是不是个好主意;您还可以看看boost::python是如何解决这个问题的,我相信Cython也“经受住了时间的考验”。我相信有一种方法可以做到这一点,在我看来,这似乎是一个基本的功能。我不熟悉包装,所以可能我错了…再次感谢你的帮助!我不熟悉
move
语义,所以在研究您的解决方案之前,我首先需要了解它。非常好。如何保证Cython中C++编译器支持移动?有可能对它进行条件编译吗?@StephaneMartin我认为Cython中的条件编译不是很容易。你最好用一个小的C++ SIMM来测试这个版本,使用<代码>、*CPLUS PLUS 宏,然后使用<代码> STD::移动< /C>或在不支持移动的情况下复制。不过,我越来越倾向于假设编译器确实支持C++11(因此也支持move)。
#ifndef PEAK_H
#define PEAK_H
class Peak {
public:
float freq;
float mag;
Peak() : freq(), mag() {}
Peak(float f, float m) : freq(f), mag(m) {}
};
#endif
# distutils: language = c++
# distutils: sources = peak_detection.cpp
from libcpp.vector cimport vector
from libcpp.map cimport map
from libcpp.string cimport string
cdef extern from "peak.hpp":
cdef cppclass Peak:
Peak()
Peak(Peak &)
float freq, mag
cdef class PyPeak:
cdef Peak *thisptr
def __cinit__(self):
self.thisptr = new Peak()
def __dealloc__(self):
del self.thisptr
cdef copy(self, Peak &other):
del self.thisptr
self.thisptr = new Peak(other)
def __repr__(self):
return "<Peak: freq={0}, mag={1}>".format(self.freq, self.mag)
property freq:
def __get__(self): return self.thisptr.freq
def __set__(self, freq): self.thisptr.freq = freq
property mag:
def __get__(self): return self.thisptr.mag
def __set__(self, mag): self.thisptr.mag = mag
cdef extern from "peak_detection.hpp":
cdef cppclass PeakDetection:
PeakDetection(map[string,string])
vector[Peak] getPeaks(vector[float])
cdef class PyPeakDetection:
cdef PeakDetection *thisptr
def __cinit__(self, map[string,string] config):
self.thisptr = new PeakDetection(config)
def __dealloc__(self):
del self.thisptr
def getPeaks(self, data):
cdef Peak peak
cdef PyPeak new_peak
cdef vector[Peak] peaks = self.thisptr.getPeaks(data)
retval = []
for peak in peaks:
new_peak = PyPeak()
new_peak.copy(peak) # how can I avoid that copy?
retval.append(new_peak)
return retval
cdef extern from "<utility>":
vector[Peak]&& move(vector[Peak]&&) # just define for peak rather than anything else
cdef class PyPeakVector:
cdef vector[Peak] vec
cdef move_from(self, vector[Peak]&& move_this):
self.vec = move(move_this)
def __getitem__(self,idx):
return PyPeak2(self,idx)
def __len__(self):
return self.vec.size()
cdef class PyPeak2:
cdef int idx
cdef PyPeakVector vector # keep this alive, since it owns the peak rather that PyPeak2
def __cinit__(self,PyPeakVector vec,idx):
self.vector = vec
self.idx = idx
cdef Peak* getthisptr(self):
# lookup the pointer each time - it isn't generally safe
# to store pointers incase the vector is resized
return &self.vector.vec[self.idx]
# rest of functions as is
# don't define a destructor since we don't own the Peak
cdef class PyPeakDetection:
# ...
def getPeaks(self, data):
cdef Peak peak
cdef PyPeak new_peak
cdef vector[Peak] peaks = self.thisptr.getPeaks(data)
retval = PyPeakVector()
retval.move_from(move(peaks))
return retval