如何使用Cython公开将C ++对象返回给Python的函数? [英] How to expose a function returning a C++ object to Python using Cython?

查看:150
本文介绍了如何使用Cython公开将C ++对象返回给Python的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Cython构建C ++-> Python绑定,但是我找不到如何从Python方法返回C ++对象的方法。

I'm building C++<->Python bindings using Cython, and I cannot find how to return a C++ object from a Python method.

更多具体来说,当编译如下所示的 peak_detection_.pyx 时,我得到

More specifically, when compiling peak_detection_.pyx, shown below, I get

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

最后一行

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

我理解该错误,但是我不介意有关如何解决该错误的帮助/指针。

I understand the error, but I would not mind some help/pointers about how to fix it.

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)


推荐答案

您的问题是cython不知道如何自动将C ++对象 Peak 转换为python包装的版本 PyPeak

Your problem here is that cython doesn't know how to automatically convert the C++ object Peak into the python wrapped version PyPeak.

一个将复制 Peak实例的版本 getPeaks 返回到 PyPeak 实例列表的code>为:

A version which will copy the instances of Peak that getPeaks returns into a list of PyPeak instances would be:

# 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

一旦编译并运行,我们将获得预期的输出:

Once compiled and run we get the expected output:

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>]

这篇关于如何使用Cython公开将C ++对象返回给Python的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆