Указатель возвращаемого объекта: «Не удается преобразовать« Координата * »в объект Python»

В настоящее время я пытаюсь цитонизировать свои классы c ++ для использования в python, и я начал с моего простого класса, но я застрял с возвращением указателя. Моя попытка решить эту проблему состояла в том, чтобы добавить конструктор копирования и вернуть версию Python класса C ++, но безуспешно.

Я получаю следующую ошибку:

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

вот мои файлы

coordinate.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);
};

coordinate.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);
}

coordinate.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

Решение

Одна проблема заключается в том, что синтаксис Cython несколько вводит в заблуждение: если def-функция определяется как, например, (потому что ваш пример не минимален, я придумываю несвязанный):

def twice(double d):
return 2.0*d

затем параметр передается в эту (python) функцию не как C-double, а как обычный Python-объект. Однако раннее связывание приведет к тому, что Cython попытается преобразовать этот объект Python в C-double с помощью __pyx_PyFloat_AsDouble во время выполнения, как первое, что вызывается при вызове функции — все это происходит за кулисами, так что, как только вы закодируете это обманчивое заполнение, вы действительно передадите двойную функцию.

Тем не менее, это автоматическое преобразование возможно только для некоторых типов — наиболее заметно double, int, cdef-классы и тд. Для других типов это невозможно, например, для необработанных указателей (что также означает указатели на пользовательские cpp-классы) — ничего подобного __pyx_PyFloat_AsDouble,

Например

def twice(double *d):
pass

не может быть cythonized, потому что необработанный указатель не может быть автоматически преобразован из / в объект python, который необходим для функции python.

Можно было бы определить cdef работать с raw-указателями, потому что они не более чем простые C-функции, таким образом

cdef twice(double *d):
pass

будет компилировать. Это, однако, не поможет вам __cinit__потому что это должно быть def-функции.

К сожалению, Cython не показывает все ошибки в вашем коде, он только обнаруживает первый раз — в противном случае он показывает, что все ваши defс Coordinate *coordinate не действительны.

Как это решить? В основном вы должны использовать свой cdef-wrapper-класс PyCCoordinate в подписи, а затем thisptr-для расчетов, например:

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

Это решение, очевидно, не будет работать для построения объекта, как в методе destination — Вы должны построить PyCCoordinate-объект, прежде чем вы сможете использовать его! Возможным решением было бы иметь конструктор, который бы создавал объект-оболочку с thisptr являющийся 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 ++, не знает перегрузки, поэтому вы не можете определить два разных конструктора (и не можете определить один из них как private), поэтому отправка должна быть выполнена вручную, например:

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
1

Другие решения

Других решений пока нет …