blint587 blint587 - 4 months ago 30
C++ Question

Cython: invalid operand types

I have a C++ class implementation which I want to expose to Python with Cython.
The class interface is something like this (each operator's implementation involves some private attributes so they cannot be inline implementations):

class Quantity {

private:
// Some implementation --

public:

explicit Quantity(...);
Quantity(const Quantity &);
~Quantity(){};
double operator()(const std::string) const;

friend Quantity operator+ (const Quantity & a, const Quantity & b) {//implementation };
friend Quantity operator- (const Quantity & a, const Quantity & b) {//implementation};
friend Quantity operator* (const Quantity & a, const Quantity & b) {//implementation};
friend Quantity operator/ (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator < (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator <= (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator > (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator >= (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator == (const Quantity & a, const Quantity & b) {//implementation};
friend bool operator != (const Quantity & a, const Quantity & b) {//implementation};

};


.pxd (partial):

from libcpp.string cimport string
from libcpp cimport bool

cdef extern from "quantity.h" namespace "munits":

cdef cppclass Quantity:
Quantity(...)

bool operator< (const Quantity &)
double operator()(string)

Quantity operator+(const Quantity &)


.pyx (partial) :

cdef class PyQuantity:

cdef :
Quantity *_thisptr

def __cinit__(PyQuantity self, ... ):
self._thisptr = new Quantity(...)

def __cinit__(PyQuantity self, Quantity ot):
self._thisptr = new Quantity(ot)

def __dealloc__(self):
if self._thisptr != NULL:
del self._thisptr

cdef int _check_alive(self) except -1:
if self._thisptr == NULL:
raise RuntimeError("Wrapped C++ object is deleted")
else:
return 0

def __enter__(self):
self._check_alive()
return self

def __exit__(self, exc_tp, exc_val, exc_tb):
if self._thisptr != NULL:
del self._thisptr
self._thisptr = NULL # inform __dealloc__
return False # propagate exceptions

def __richcmp__(PyQuantity self, PyQuantity other, op):
if op == 0:
return self._thisptr[0] < other._thisptr[0]


def __add__(PyQuantity self, PyQuantity other):

return new PyQuantity(self._thisptr[0] + other._thisptr[0])


The implementation for the operator() and all the comparison operators work but for other math operators like '+' I can't get it right. I also checked variations described here: Cython: Invalid operand types for '+' (btVector3; btVector3)
But I'm still getting either Invalid operand types or Cannot convert 'Quantity' to Python object. What am I missing, why do other operators work and addition and such not?

Answer

Multiple __cinit__ is not allowed as far as I remember

def __cinit__(PyQuantity self, ... ):
    self._thisptr = new Quantity(...)

def __cinit__(PyQuantity self, Quantity ot):
    self._thisptr = new Quantity(ot)

Arguments to __cinit__ should be python objects (object, list, tuple, int, bint, double, PyQuantity, ...) and not a C++ class Quantity.

def __cinit__(PyQuantity self, PyQuantity other=None):
    if other is not None:
        self._thisptr = new Quantity(other._thisptr[0])
    else:
        self._thisptr = new Quantity()

This

def __add__(PyQuantity self, PyQuantity other):
    return new PyQuantity(self._thisptr[0] + other._thisptr[0])

could probably be written as

def __add__(PyQuantity self, PyQuantity other):
    cdef PyQuantity nobj = PyQuantity(self)
    nobj._thisptr[0] += other._thisptr[0]

    return nobj