I am trying to Cython-wrap a dll written in C++ with the following header file:
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
struct cmplx64
{
float re;
float im;
};
EXTERN_DLL_EXPORT int foo(cmplx64 *arr, int arr_sz);
The PXD file:
cdef extern from "mylib.h":
cdef struct cmplx64:
np.float64_t re
np.float64_t im
int foo(cmplx64 *arr, int arr_sz) except +
The PYX file:
cimport cmylib
import numpy as np
cimport numpy as np
import cython
def foo(np.ndarray[np.complex64_t, ndim=1] arr, int arr_sz):
return cmylib.foo(&arr[0], arr_sz)
The problem does not appear with my setup file.
In lieu of the struct definition, I have tried building a cppclass per a suggestion I found, but I did not get as far with that as this current method.
The error message I am getting is:
Cannot assign type 'float complex *' to 'complexFloatStruct *'
My problem is caused by the fact that the author of the library I'm using defined a complex type with a struct rather than simply using the built in complex type in the C++ std library. If that were the case, I would have no issue.
However, it seems perfectly reasonable that I should be able to wrap a C++ class or struct with Cython. I have been over the documentation and have pretty much failed. Thanks for your help!
A simple cast might be sufficient,
def foo(np.ndarray[np.complex64_t, ndim=1] arr, int arr_sz):
return cmylib.foo(<cmylib.cmplx64 *>&arr[0], arr_sz)
Related
I am new to the Cython/C++ interface and currently studying the examples in the documentation. I am trying to find a way to wrap a C++ struct (instead of C/C++ native types) that is used as the type specifier of the argument of a C++ class constructor. Then I can instantiate a C++ class on the Python side.
First, the rectangle.cpp file which is a simplified version of the example with only constructors. Here I introduce a struct to be used as the type of the argument for the constructor. (I merged the .h header file with .cpp just to make the post shorter. This compiles normally,though.)
rectangle.cpp
#include <iostream>
namespace shapes {
struct Point {
int x0;
int y0;
int x1;
int y1;
};
class Rectangle {
public:
int x0, y0, x1, y1;
Rectangle();
Rectangle(Point point);
~Rectangle();
};
// Default constructor
Rectangle::Rectangle () {}
Rectangle::Rectangle (Point point) {
this->x0 = point.x0;
this->y0 = point.y0;
this->x1 = point.x1;
this->y1 = point.y1;
}
// Destructor
Rectangle::~Rectangle () {}
}
Second, the rectangle.pxd file which is basically a copy from the example, except that I included a cdef struct Point to declare the structure. (Not sure if this is a correct approach.)
rectangle.pxd
# Declare the class with cdef
cdef extern from "rectangle.cpp" namespace "shapes":
cdef struct Point:
int x0
int y0
int x1
int y1
cdef cppclass Rectangle:
Rectangle() except +
Rectangle(Point) except + # Not sure if this is correct
int x0, y0, x1, y1
Third, the rect.pyx file
rect.pyx
# distutils: language = c++
from rectangle cimport Rectangle, Point
# Create a Cython extension type which holds a C++ instance
# as an attribute and create a bunch of forwarding methods
# Python extension type.
cdef class PyRectangle:
cdef Point point # Not sure if this is correct
cdef Rectangle c_rect # Hold a C++ instance which we're wrapping
def __cinit__(self, Point point):
self.c_rect = Rectangle(point)
Finally, the setup.py which is identical to the example.
setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("rect.pyx"))
If I compile using the standard command python3 setup.py build_ext --inplace, the following error pops out. (The original example can compile and run without any problem.)
[1/1] Cythonizing rect.pyx
Error compiling Cython file:
------------------------------------------------------------
...
PyTypeObject *Py_TYPE(obj)
bint PyMapping_Check(obj)
object PyErr_Format(exc, const char *format, ...)
#cname("__pyx_convert__from_py_shapes::Point")
cdef Point __pyx_convert__from_py_shapes::Point(obj) except *:
^
------------------------------------------------------------
FromPyStructUtility:11:41: Expected an identifier or literal
Traceback (most recent call last):
File "setup.py", line 5, in <module>
setup(ext_modules=cythonize("rect.pyx"))
File "/usr/lib/python3/dist-packages/Cython/Build/Dependencies.py", line 877, in cythonize
cythonize_one(*args)
File "/usr/lib/python3/dist-packages/Cython/Build/Dependencies.py", line 997, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: rect.pyx
I am sure there are mistakes in wrapping the struct, since the error is triggered by FromPyStructUtility. But I haven't found a straightforward answer/solution to this particular scenario. If I want to keep the C++ code as it is, i.e., insisting on passing a struct to the constructor, how do I implement this on the Cython side? Your comments/suggestions/tutorials will be highly appreciated.
I am fighting with CYTHON, trying to make a copy of a C++ class in a Python class.
I the .pxd I have:
cdef extern from "MyCppClass.h":
cdef cppclass MyCppClass:
MyCppClass ()
int Copy(MyCppClass * Source)
cdef class MyPyClass:
cdef MyCppClass * thisptr
cdef copy(self, MyCppClass *s)
and in the .pyx I have
cdef class MyPyClass:
def __init__(self):
self.thisptr = new MyCppClass ()
cdef copy(self, MyCppClass * s):
self.thisptr.Copy(s)
The compilation is ok.
Then I try to use it in another Cython module:
A=new MyCppClass()
pA=MyPyClass()
… do some stuff on A
p1.copy(A)
And I get the usual “cannot convert to Python object”
I have tried a lot of things to copy, like :
cdef getThisptr(self):
return self.thisptr
A=new MyCppClass()
pA=MyPyClass()
B=pA.getThisptr()
B.Copy(A)
But nothing works!
It sounds very basic in Cython, but I am still waiting for a solution…
You're missing cimport. In order for Cython to know about your cdef defined types and functions you need to add
from module_name cimport MyPyClass
Otherwise it just assumes that Copy is a Python function accepting a Python object.
The relevant bit of documentation
So I am writing a Python API for a C++ library using Cython. I have three classes with almost identical functionality: A, B, and C. This difference is only how one of their objects is built on initialization and some constants.
Originally, I have wrote them all out as separate classes and then could define them via extern in my cython code. This compiles and works well but there is a lot of repeated code and I would really like this project to be more DRY.
So I decided to write a base class for A, B, and C that implemented most of the functionality once. However, I needed to template that base class and this is causing me a nightmare when I try to define everything in Cython. Here is a toy example of what I'm talking about (ignore missed semi-colons etc, if you find them). This is my "classes.h" file
int library_method_load(std::string file_name){
return std::string.length();
}
template <class T>
class BaseClass{
public:
T important_obj;
BaseClass(std::string file_name){ important_obj = library_method(file_name);};
virtual T library_method(std::string file_name) = 0;
// Important logicks...
~BaseClass(){};
}
class A : public BaseClass<int> {
A(std::string file_name): BaseClass<int>(file_name){};
int library_method(std::string file_name){ return library_method_load(file_name);};
~A(){};
}
When I try to wrap this, if I don't tell cython about the base class, I get undefined symbols. If I try to define the base class, the templating causes problems. The latter could be due to the fact that I don't know the syntax properly for inheriting templated base classes.
Here is my current attempt
#distutils: language = c++
from libcpp.string cimport string
cdef extern from "classes.h":
cppclass BaseClass[T]:
BaseClass(string file_name)
cdef extern from "classes.h":
cppclass A(BaseClass[int]):
A(string file_name)
cdef class PyBase:
cdef BaseClass* wrapped
cdef class PyA(PyBase):
def __cinit__(self, string file_name):
self.wrapped = <BaseClass[int]*> new A(file_name)
Doing this gives me the following compiler error:
Error compiling Cython file:
------------------------------------------------------------ ...
cdef class PyA(PyBase):
def __cinit__(self, string file_name):
self.wrapped = <BaseClass[int]*> new A(file_name)
^
wrapper.pyx:23:23: Cannot assign type 'BaseClass[int] *' to
'BaseClass[T] *' Traceback (most recent call last): File "setup.py",
line 9, in
setup(name="test", version="1.0.0", ext_modules=cythonize([rk])) File
"/home/jacob/anaconda3/lib/python3.6/site-packages/Cython/Build/Dependencies.py",
line 1027, in cythonize
cythonize_one(*args) File "/home/jacob/anaconda3/lib/python3.6/site-packages/Cython/Build/Dependencies.py",
line 1149, in cythonize_one
raise CompileError(None, pyx_file) Cython.Compiler.Errors.CompileError: wrapper.pyx
Does anyone know how to do this? Clearly my template substituion is off. Should I stick with the repetitive code instead? Are there other clever solutions?
One solution is to drop PyBase because in cdef BaseClass* wrapped the template argument for BaseClass is missing, which makes the line meaningless.
E.g.:
cdef class PyA(PyBase):
cdef A* wrapped
def __cinit__(self, string file_name):
self.wrapped = new A(file_name)
(I am not sure though if you can pass a C++ object into __init__ and __cinit__)
I’m trying to pass a Structure from a Matlab function in Simulink to an external C++ function using the coder.ceval() and the coder.cstructname(). When I try to run the code on an Arduino Due board using the deploy to hardware tool in Simulink I get the error:
error: invalid use of incomplete type 'struct MyStruct'
error: forward declaration of 'struct MyStruct'
I’m using the example code from mathworks but use a c++ function instead of a c function:
Header use_struct.h:
#include <tmwtypes.h>
typedef struct MyStruct
{
double s1;
double s2;
} MyStruct;
void use_struct(struct MyStruct *my_struct);
C++ function use_struct.cpp:
#include <stdio.h>
#include <stdlib.h>
// #include "use_struct.h" // Doesn’t work when I include it here
extern “C” void use_struct(struct MyStruct *my_struct)
{
double x = my_struct->s1;
double y = my_struct->s2;
}
Matlab function:
structVar.s1 = 1;
structVar.s2 = 2;
if strcmp(coder.target,'rtw'),
coder.cinclude('use_struct.h');
coder.cstructname(structVar, 'MyStruct', 'extern');
coder.ceval('use_struct', coder.ref(structVar));
end
I need it to be a C++ function for the later code. However I also tried it with a c function without the extern “C” but it doesn’t work anyway. Can anyone help me with this problem?
I found the solution. I had to include the c header use_struct.h in the use_struct.cpp also with:
extern "C"
{
#include "use_struct.h"
}
This seems like it should be a very simple question, and I've seen similar discussions here, but nothing that quite tackles this problem. I have a class written in c++ that I would like to access with cython. The simple example below illustrates the problem, it compiles just fine, however, I get an ImportError when I use it.
//element.h
template <typename T>
class element{
public:
element(T);
~element();
T data;
};
and
//element.cc
#include "element.h"
template <typename T>
element<T>::element(T _data){
data = _data;
}
template <typename T>
element<T>::~element(){
}
it's accessed through the following simple cython
cdef extern from "element.h":
cdef cppclass element[T]:
element(T) except +
T data
cdef element[int] *el = new element[int](3)
print el.data
and compiled in place with
from distutils.core import setup, Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("example",
['example.pyx','./element.cc'],
language = "c++")]
setup(cmdclass = {'build_ext':build_ext},
name = 'example',
ext_modules = ext_modules)
However, when I try to import the resulting shared library, I get
ImportError: .../example.so: undefined symbol: _ZN7elementIiEC1Ei
If I simply strip out all the templating and force ints for example, the code compiles just fine (as before) but this time it runs. So, in other words, this works fine.
//element.h
class element{
public:
element(int);
~element();
int data;
};
and
//element.cc
#include "element.h"
element::element(int _data){
data = _data;
}
element::~element(){
}
and
//example.pyx
cdef extern from "element.h":
cdef cppclass element:
element(int) except +
int data
cdef element *el = new element(3)
print el.data
What am I doing wrong with the templating in the first case?
You need to implement template in one header file instead of split into element.cc. When I run c++file command it shows that compiler failed to link element constructor definition
$ c++filt _ZN7elementIiEC1Ei
element<int>::element(int)