The code that I show below works, but I am not exactly sure why.
I am using:
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.cc_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 namespacesetup.py
: a distutils file which performs the compiling of the cython codeand the process produces two files
c_adder.c
: an intermediate cython c filec_adder.so
: the python module which can be imported into a namespaceThe 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.
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?
The cimport statement is used in a definition or implementation file to gain access to names declared in another definition file. Its syntax exactly parallels that of the normal Python import statement. When pure python syntax is used, the same effect can be done by importing from special cython.
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.
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