2015-11-06 62 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

相關問題