如何使用 Cython 向 Python 公开返回 C++ 对象的函数?
Posted
技术标签:
【中文标题】如何使用 Cython 向 Python 公开返回 C++ 对象的函数?【英文标题】:How to expose a function returning a C++ object to Python using Cython? 【发布时间】:2015-11-06 18:11:34 【问题描述】:我正在使用 Cython 构建 C++Python 绑定,但我找不到如何从 Python 方法返回 C++ 对象。
更具体地说,在编译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)
【问题讨论】:
难道没有cpdef
或类似的东西可以使cython
和python
都可以看到函数吗?
确实如此,但显然没有帮助:我得到了同样的编译错误。
【参考方案1】:
您的问题是 cython 不知道如何自动将 C++ 对象 Peak
转换为 python 包装版本 PyPeak
。
将getPeaks
返回的Peak
实例复制到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>]
【讨论】:
谢谢西蒙。我必须复制 Peak 实例吗?没有办法获取引用或定义 C++ 对象的接口? 问题在于,当您离开getPeaks
的python 版本时,向量将超出范围,并将释放所有属于它的Peak
实例。解决此问题的一种方法是让getPeaks
返回一个指向Peak
实例的指针 向量,然后您可以将其分配给PyPeak
实例中的thisptr
。
但是返回一个指针向量会导致指针悬空,对吧?
你的 PyPeak
实例有一个 __dealloc__
方法正是为此。当每个实例的引用计数变为零时,它将释放指向 Peak
对象的指针。以上是关于如何使用 Cython 向 Python 公开返回 C++ 对象的函数?的主要内容,如果未能解决你的问题,请参考以下文章
使用 cython 向 python 公开一个 c++ 函数