Minimal example of wrapping C code with Cython- passing int and struct
c, cython, python, word-wrap
Solution
The `adder.c` file needs to be referenced somewhere so it knows where to find the implementation of `add`. As you discovered, listing `adder.c` as an additional source file works. A more common way to use external (shared) C libraries is to compile it as libadder.so and link it via the `libraries` parameter in your Extension declaration.
For something this simple you could also just write
cdef extern from "adder.c": # note the .c
int add(int x, int y)
...
or put the entire implementation in the .h file.
Problem
The code that I show below works, but I am not exactly sure why. I am using: - Mac OSX 10.8.5 - brewed Python 2.7.5 - Cython 0.20.2 This code is taken primarily from this video tutorial and git page, but unfortunately did not work for me 'out of the box'. The goal of this wrapping to provide access through Python to a trivial C function, which accepts either integers or a structure of integers and adds them together. There are 5 files needed for this, described below: - `adder.c`: C code of the two adder functions: add (scalar inputs) and pair_add (struct inputs) - `adder.h`: header file for adder.c - `c_adder.pxd`: a cython header file- essentially tells cython which parts of the main header file to pay attention to - `cy_adder.pyx`: the cython code used to define both functions in the python namespace - `setup.py`: a distutils file which performs the compiling of the cython code and the process produces two files - `c_adder.c`: an intermediate cython c file - `c_adder.so`: the python module which can be imported into a namespace The input files are below: adder.c ``` #include <stdlib.h> #include "adder.h" int pair_add(PAIR * ppair) { return ppair->x + ppair->y; } int add(int x, int y) { return x + y; } ``` adder.h ``` typedef struct { int x; int y; } PAIR; int pair_add(PAIR * ppair); int add(int, int); ``` c_adder.pxd ``` cdef extern from "adder.h": ctypedef struct PAIR: int x int y int add(int x, int y) int pair_add(PAIR * ppair) ``` cy_adder.pyx ``` cimport c_adder def add(x, y): return c_adder.add(x, y) def pair_add(x, y): cdef c_adder.PAIR pair pair.x = x pair.y = y return c_adder.pair_add(&pair) ``` setup.py ``` from setuptools import setup from setuptools.extension import Extension from Cython.Distutils import build_ext from Cython.Build import cythonize ext_modules = cythonize([Extension("cy_adder", ["cy_adder.pyx",'adder.c'])]) setup( name = 'Hello world app', cmdclass = {'build_ext': build_ext}, ext_modules = ext_modules ) ``` I was able to successfully create a .so file by running ``` $ python setup.py build_ext --inplace ``` In the same directory as the above files. It is then possible to load `cy_adder` into the namespace of a python interpreter. The question In the setup declaration, I have included `adder.c` as an auxiliary module. If I don't do this I get the following error when I import the `.so` file: ``` ImportError: dlopen(./cy_adder.so, 2): Symbol not found: _add Referenced from: ./cy_adder.so Expected in: flat namespace in ./cy_adder.so ``` Is there a step that I am missing which would mean that I don't need to explicitly pass `adder.c` along with my setup command? Am I opening myself up to some potential instability by doing this?