I wrote a minimal C function (with accompanying header file) with the goal of creating a Cython wrapper that would enable the C code's use from a Python file somewhere else.
My files are as such:
C file:
/* engine.c */
#include <stdio.h>
#include "customlib.h"
int engine(int value, int iterations) {
/* declare iteration index */
int idx;
for (idx = 0; idx < iterations; idx = idx + 1) {
value = value * 3;
}
/* return value that should be accessible by the Python module */
return value;
}
C header file:
/* customlib.h */
#ifndef CUSTOM_HEADER_H
#define CUSTOM_HEADER_H
/* engine function */
int engine(int value, int iterations);
#endif
Cython module that wraps the C code:
# wrapper.pyx
cdef extern from "customlib.h":
int engine(int value, int iterations)
def call_c_code(value, iterations):
output_value = engine(value, iterations)
print(output_value)
Python module that calls the C code via the Cython wrapper:
# caller.py
import wrapper
wrapper.call_c_code(1, 2) /* values not important; just an example */
Setup code for generating the *.so from the *.pyx file:
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
# definitions
sourcefiles = ['engine.c']
extensions = [Extension('wrapper', sourcefiles)]
# setup function
setup(name='wrapper', ext_modules=cythonize('wrapper.pyx'))
THE PROBLEM: The shared object (*.so) seems to compile without any issues. However, even just importing the wrapper.py
throws the following error:
Traceback (most recent call last):
File "caller.py", line 10, in <module>
import wrapper
ImportError: /home/alex/Documents/Projects/Cython/wrapper.cpython-36m-x86_64-linux-gnu.so: undefined symbol: engine
In your setup.py
you don't actually do anything with:
extensions = [Extension('wrapper', sourcefiles)]
this is just dead code, essentially. It assigns to a variable and then never uses it. Regardless, you don't want to make an Extension
from your engine.c
. Extension
is for defining Python modules. The only Python module here is the one compiled from wrapper.pyx
.
Instead, try something like:
extensions = [Extension('wrapper', ['wrapper.pyx', 'engine.c'])]
setup(
...
ext_modules=cythonize(extensions)
...
)
This will also compile engine.c
and link its resulting object file into your extension module.
The cythonize
helper function is smart enough to distinguish plain .c
sources from Cython sources that it needs to pass through the Cython compiler. It will then pass all the resulting C sources through the C compiler and link them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With