I'd like to convert some ctypes code to use cython instead, but I'm struggling. Essentially, the ctypes code:
- copies the contents (floats) of two lists into C-compatible structs
- sends the structs to my binary via FFI
- receives the structs back (the length is never modified)
- copies the contents to two new lists
- sends the structs back across the FFI boundary so their memory can be freed
My ctypes code looks like this so far:
rlib.h
#ifndef RLIB_H
#define RLIB_H
typedef struct _FFIArray {
void* data;
size_t len;
} _FFIArray;
typedef struct _Result_Tuple {
_FFIArray e;
_FFIArray n;
} _Result_Tuple;
_Result_Tuple convert_to_bng_threaded(_FFIArray x, _FFIArray y);
void drop_float_array(_FFIArray x, _FFIArray y)
#endif
mylib.pxd
cdef extern from "rlib.h":
struct _FFIArray:
void* data
size_t len
struct _Result_Tuple:
_FFIArray e
_FFIArray n
cdef _Result_Tuple convert_to_bng_threaded(_FFIArray x, _FFIArray y)
cdef void drop_float_array(_FFIArray x, _FFIArray y)
util_cython.pyx
import cython
from mylib cimport _FFIArray, convert_to_bng_threaded, drop_float_array
def call_convert_bng(_FFIArray x, _FFIArray y):
return convert_to_bng_threaded(x, y)
def call_drop_float_array(_FFIArray x, _FFIArray y):
return drop_float_array(x, y)
setup.py
from setuptools import setup, Extension, find_packages
from Cython.Build import cythonize
from Cython.Distutils import build_ext
ext = Extension('util_cython',
sources=['util_cython.pyx'],
libraries=['latlon_bng',],
library_dirs=['.',],
include_dirs=['.']
)
extensions = [ext,]
setup(
name = "util_cython",
ext_modules = cythonize(extensions),
cmdclass={'build_ext': build_ext},
)
I have a few questions about how to proceed:
Firstly, the compilation step is currently failing:
python setup.py build_ext --inplace
Compiling util_cython.pyx because it changed.
[1/1] Cythonizing util_cython.pyx
Error compiling Cython file:
------------------------------------------------------------
...
import cython
from mylib cimport _FFIArray, convert_to_bng_threaded, drop_float_array
def call_convert_bng(_FFIArray x, _FFIArray y):
return convert_to_bng_threaded(x, y)
^
------------------------------------------------------------
util_cython.pyx:5:34: Cannot convert '_Result_Tuple' to Python object
Traceback (most recent call last):
File "setup.py", line 17, in <module>
ext_modules = cythonize(extensions),
File "/Users/sth/dev/cythonize_test/venv/lib/python2.7/site-packages/Cython/Build/Dependencies.py", line 912, in cythonize
cythonize_one(*args)
File "/Users/sth/dev/cythonize_test/venv/lib/python2.7/site-packages/Cython/Build/Dependencies.py", line 1034, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: util_cython.pyx
Why is Cython failing to convert _Result_tuple
?
Secondly, how do I define a cython function to accept lists (or arrays; anything that supports __iter__
), and copy their contents into _FFIArray
structs, so I can call call_convert_bng
?
The following is untested, because I don't have the library code and have had to take some guesses as to how it works. But it should give you an idea as to how you go about it.
I'd start by using a Python array type to store your input/output. Either the standard library array type, or numpy arrays. These store the arrays continuously in memory (like C would) and so provided the
_FFIArray
data
attribute can just be pointed at this memory for input.