2015-11-06 84 views
4

我正在构建C++ < - >使用Cython进行Python绑定,我无法找到如何从Python方法返回C++对象。如何使用Cython公开一个将C++对象返回给Python的函数?

更具体地说,编译peak_detection_.pyx时,如下图所示,我得到

peak_detection_.pyx:35:36: Cannot convert 'vector[Peak]' to Python object 

最后行

def getPeaks(self,data): 
    return self.thisptr.getPeaks(data) 

我理解错误,但我不会介意如何一些帮助/指针要解决这个问题。

peak_detection.hpp

#ifndef PEAKDETECTION_H 
#define PEAKDETECTION_H 

#include <string> 
#include <map> 
#include <vector> 

#include "peak.hpp" 


class PeakDetection 
{ 
    public: 
     PeakDetection(std::map<std::string, std::string> config); 
     std::vector<Peak> getPeaks(std::vector<float> &data); 

    private: 
     float _threshold;    
}; 

#endif 

peak_detection.cpp

#include <iostream> 
#include <string> 

#include "peak.hpp" 
#include "peak_detection.hpp" 


using namespace std; 


PeakDetection::PeakDetection(map<string, string> config) 
{ 
    _threshold = stof(config["_threshold"]); 
} 

vector<Peak> PeakDetection::getPeaks(vector<float> &data){ 

    Peak peak1 = Peak(10,1); 
    Peak peak2 = Peak(20,2); 

    vector<Peak> test; 
    test.push_back(peak1); 
    test.push_back(peak2); 

    return test; 
} 

peak.hpp

#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 

peak_detection_.pyx

# 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() 

cdef class PyPeak: 
    cdef Peak *thisptr 
    def __cinit__(self): 
     self.thisptr = new Peak() 
    def __dealloc__(self): 
     del self.thisptr 

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): 
     return self.thisptr.getPeaks(data) 
+0

”不是个还有一个'cpdef',或者类似的东西,这使得'cython'和'python'都可以看到函数? – hpaulj

+0

的确,但它并没有明显的帮助:我得到相同的编译错误。 – jul

回答

3

你这里的问题是,用Cython不知道如何自动转换C++对象Peak到封装版本,PyPeak蟒蛇。

PeakgetPeaks回报的情况下复制到PyPeak实例列表的版本是:

# 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) 
      retval.append(new_peak) 

     return retval 

一旦编译和运行我们得到预期的输出:

In [1]: import peak_detection_ 

In [2]: print peak_detection_.PyPeakDetection({"_threshold" : "0.01"}).getPeaks([1,2,3]) 
[<Peak: freq=10.0, mag=1.0>, <Peak: freq=20.0, mag=2.0>] 
+0

谢谢西蒙。我必须复制Peak实例吗?没有办法获得一个引用或定义一个C++对象的接口? – jul

+1

问题是,当你离开Python版本的'getPeaks'时,向量将超出范围,并将释放属于它的所有'Peak'实例。解决这个问题的一种方法是'getPeaks'将*指针向量返回到'Peak'实例,然后您可以将其分配给'PyPeak'实例的'thisptr'。 –

+0

但是,返回一个指针向量会导致悬挂指针,对吧? – jul

相关问题