代码之家  ›  专栏  ›  技术社区  ›  Westranger

返回对象指针:“无法将“坐标*”转换为Python对象”

  •  1
  • Westranger  · 技术社区  · 6 年前

    我目前正在尝试将我的c++类cythonize为python中的usabe,我开始使用我的一个简单类,但我一直在返回指针。我解决这个问题的最佳方法是添加一个复制构造函数并返回C++类的Python版本,但没有成功。

    我得到以下错误:

    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    
        def bearing(self, Coordinate *coordinate):
            return self.thisptr.bearing(coordinate)
    
        def destination(self, double bearing, double distance):
            return PyCCoordinate(self.thisptr.destination(bearing, distance))
                                                        ^
    ------------------------------------------------------------
    
    ./coordinate.pyx:32:53: Cannot convert 'Coordinate *' to Python object
    

    这是我的文件

    坐标H

    class Coordinate {
    private:
        double m_x = 0;
        double m_y = 0;
    public:
        Coordinate(const double x, const double y);
    
        Coordinate(const Coordinate *coord);
    
        void setX(const double value);
    
        void setY(const double value);
    
        double getX();
    
        double getY();
    
        double distance(Coordinate *coord);
    
        double bearing(Coordinate *coord);
    
        Coordinate *destination(const double bearing, const double distance);
    };
    

    坐标cpp公司

    #include <cmath>
    #include "coordinate.h"
    
    Coordinate::Coordinate(const double x, const double y) {
        m_x = x;
        m_y = y;
    }
    
    Coordinate::Coordinate(const Coordinate *coord) {
        m_x = coord->x;
        m_y = coord->y;
    }
    
    
    void Coordinate::setX(const double value) {
        m_x = value;
    }
    
    void Coordinate::setY(const double value) {
        m_y = value;
    }
    
    double Coordinate::getX() {
        return m_x;
    }
    
    double Coordinate::getY() {
        return m_y;
    }
    
    double Coordinate::distance(Coordinate *coord) {
        return sqrt(pow(m_x - coord->getX(), 2.0) + pow(m_y - coord->getY(), 2.0));
    }
    
    double Coordinate::bearing(Coordinate *coord) {
        const double len_x = coord->getX() - m_x;
        const double len_y = coord->getY() - m_y;
        double res = atan2(len_y, len_x);
        if (res < 0.0) {
            res += 2.0 * M_PI;
        }
        return res;
    }
    
    Coordinate *Coordinate::destination(const double bearing, const double distance) {
        double new_x = m_x + cos(bearing) * distance;
        double new_y = m_y + sin(bearing) * distance;
        return new Coordinate(new_x, new_y);
    }
    

    坐标pxy公司

    cdef extern from "coordinate.h":
        cdef cppclass Coordinate:
            Coordinate(const double x, const double y) except +
            Coordinate(const Coordinate *coord) except +
    
            void setX(const double value)
            void setY(const double value)
            double getX()
            double getY()
            double distance(Coordinate *coord)
            double bearing(Coordinate *coord)
            Coordinate *destination(const double bearing, const double distance)
    
    cdef class PyCCoordinate:
        cdef Coordinate *thisptr
        def __cinit__(self, double x, double y):
            self.thisptr = new Coordinate(x,y)
        def __cinit__(self, Coordinate* coord):
            self.thisptr = new Coordinate(coord)
        def __dealloc__(self):
            del self.thisptr        
    
        def distance(self, Coordinate *coordinate):
            return self.thisptr.distance(coordinate)
    
        def bearing(self, Coordinate *coordinate):
            return self.thisptr.bearing(coordinate)
    
        def destination(self, double bearing, double distance):
            return PyCCoordinate(self.thisptr.destination(bearing, distance))
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   ead    6 年前

    一个问题是cython语法有点误导人:如果 def -函数定义为示例(因为您的示例不是最小的,所以我创建了一个不相关的示例):

    def twice(double d):
       return 2.0*d
    

    然后,参数不是作为C-double传递给这个(python)函数,而是作为一个常见的python对象传递给它。然而,早期的绑定将导致cython试图通过 __pyx_PyFloat_AsDouble 在运行时,作为调用函数的第一件事,这一切都发生在幕后,因此作为编码器,您有这种欺骗填充,您将向函数传递一个double。

    然而,这种自动转换仅适用于某些类型,最重要的是 double ,则, int ,则, cdef -课程等等。对于其他类型,这是不可能的,例如对于原始指针(这也意味着指向自定义cpp类的指针),没有什么 __pyx\u PyFloat\u AsDouble

    例如

    def twice(double *d):
       pass
    

    无法cythonized,因为原始指针无法自动从python对象转换/转换为python对象,这是python函数所必需的。

    可以定义 cdef公司 具有原始指针的函数,因为它们不超过简单的C函数,因此

    cdef twice(double *d):
       pass
    

    将编译。但是,这对您没有帮助 __cinit__ ,因为它必须是 def公司 -功能。

    不幸的是,Cython并没有显示代码中的所有错误,只显示它找到的第一个错误,否则它会显示 def公司 s与 Coordinate *coordinate 无效。

    如何解决它?基本上你应该使用 cdef公司 -包装器类 PyCCoordinate 在签名中,然后 thisptr -例如,对于计算:

    def distance(self, PyCCoordinate coordinate):
        return self.thisptr.distance(coordinate.thisptr)
    

    这种解决方案显然不适用于对象的构造,就像在方法中一样 destination -你必须构建一个 PyCCoordinate公司 -对象,然后才能使用它!一个可能的解决方案是有一个构造函数,它将用 本PTR 存在 NULL ,调用它并手动设置此指针,类似于

    cdef class PyCCoordinate:
        cdef Coordinate *thisptr
        def __cinit__(self):
            pass
    
        def __dealloc__(self):
            del self.thisptr        
    
        def destination(self, double bearing, double distance):
            cdef PyCCoordinate res=PyCCoordinate()
            res.thisptr=self.thisptr.destination(bearing, distance)
            return res
    

    另一个问题:cython(也是python)与c++不同,它不知道重载,因此不能定义两个不同的构造函数(也不能定义其中一个私有构造函数),因此必须手动进行调度,例如:

    cdef class PyCCoordinate:
        cdef Coordinate *thisptr
    
        def __cinit__(self, x=None, y=None):
            if x is None or y is None:#default, "private" constructor
               pass     #thisptr is initialized to NULL
            else:
               self.thisptr = new Coordinate(x,y) #
    
        def __dealloc__(self):
            del self.thisptr        
    
        def destination(self, double bearing, double distance):
            cdef PyCCoordinate res=PyCCoordinate()
            res.thisptr=self.thisptr.destination(bearing, distance)
            return res