Home > Net >  Cython wrapping c class methods that return class type
Cython wrapping c class methods that return class type

Time:09-01

I have a simple C Vec3 class, but I am struggling to figure out how to wrap certain class methods with Cython. Here are the files:

vec3.h:

#ifndef VEC3_H
#define VEC3_H

struct Vec3 {
    float x, y, z;
    Vec3(float x, float y, float z);
    Vec3 cross(Vec3 other);
};

#endif

vec3.cpp:

#include "vec3.h"

Vec3::Vec3(float x, float y, float z) {
    this->x = x;
    this->y = y;
    this->z = z;
}
Vec3 Vec3::cross(Vec3 other) {
    return Vec3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);
}

vec3.pyx:

# cython: language_level=3
# distutils: language=c  

cdef extern from "vec3.h":
    cdef cppclass Vec3:
        float x, y, z
        Vec3(float x, float y, float z) except  
        Vec3 cross(Vec3 other)

cdef class py_Vec3:
    cdef Vec3 *thisptr
    def __cinit__(self, x, y, z):
        self.thisptr = new Vec3(x, y, z)
    # ...
    cpdef cross(self, Vec3 other):
        return self.thisptr.cross(other)

I first compile the C code using:

g   -c vec3.cpp -o vec3.o
ar rcs vec3.a vec3.o

Then run the following setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

vec3= Extension(
    name="vec3", sources=["vec3.pyx"], libraries=["linalg"], language="c  "
)

setup(name="vec3-test", ext_modules=cythonize([vec3]))

But I get the following error from the cross method:

Cannot convert 'Vec3f' to Python object

Is there a proper way to do this?

CodePudding user response:

you have a typo in vec3.cpp

return Vec3f(...);

remove the f. it doesnt recognize Vec3f as a class

CodePudding user response:

Becuase Vec3 is a cpp type and py_Vec3 is a python type, and they cannot be converted to each other automatically by cython. In the cpdef function

cpdef cross(self, Vec3 other):
    return self.thisptr.cross(other)

the generated python wrapper version expects a python type argument, but conversion from Vec3 to py_Vec3 fails, so does the return value.

you have to do the conversion manually. Try the following modified version:

cdef class py_Vec3:
    cdef Vec3 *thisptr
    
    def __cinit__(self, x, y, z):
        self.thisptr = new Vec3(x, y, z)
        
    cdef py_Vec3 from_vec3(self, Vec3 v):
        return py_Vec3(v.x, v.y, v.z)

    cpdef py_Vec3 cross(self, py_Vec3 other):
        return self.from_vec3(self.thisptr.cross(other.thisptr[0]))
    
    def __dealloc__(self):
        del self.thisptr
        
    def __repr__(self):
        return f"py_Vec3({self.thisptr.x}, {self.thisptr.y}, {self.thisptr.z})"
  • Related