Interfacing with External C code

Cython module can be used as a bridge to allow Python code to call C code, it can also be used to allow C code to call python code.

1
2
3
cdef extern from "spam.h":
    int spam_counter
    void order_spam(int tons)

The cdef extern from clause does three things:

  • It directs Cython to place a #include statement for the named header file in the generated C code.
  • It prevents Cython from generating any C code for the declarations found in the associated block
  • It treats all declarations within the block as though they started with cdef extern

Implementing function in C

When you want to call c code from a Cython module, usually that code will be in some external library that you link your extension against. However, you can also directly complie C(or C++) as part of your Cython module. In the .pyx file, you can put something like:

1
2
cdef extern from "spam.c":
   void order_spam(init tons)

Cython will assume that the function order_spam() is defined in the spam.c. If you also want to cimport this function from another module, it must be declared(not extern) in the .pxd file like this:

1
 cdef void order_spam(int tons)

For this to work, The signature of order_spam() in spam.c must match the signature that Cython uses, in particular the function must be static:

1
2
3
4
static void order_spam(int tons)
{
   printf("Ordered %i tons of spam!\n", tons);
}

From now on, Let’s see some example with multiple .c files and .h file to make extension module on Cython

I have add.c, add.h, main.c, substraction.c and substraction.h as C files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
## In the add.c 
int sum_function(int a, int b){
     return a + b;
}

## In the add.h
int sum_function(int a, int b);

## In the substraction.c
int substraction_function(int a, int b){

    if (a > b) {
        return a - b;
    }
    else {
        return b - a;
    }
}

## In the substraction.h
int substraction_function(int a, int b);
 
## In the main.c
#include <stdio.h>
#include "add.h"
#include "substraction.h"


int main(){
   int test_a = 2;
   int test_b = 3;
   int result = 0;


   result = sum_function(test_a, test_b);
   printf("the result of sum_function : %d\n", result);

   result = substraction_function(test_a, test_b);
   printf("the result of substraction : %d\n", result);
   return 0:
}

As you can know, main.c show you printf statement of the result using sum.c and substractioin.c.

If you want to compile the main.c, run one_shot_compiling.sh.

./one_shot_compiling.sh

1
2
3
4
# hyunyoung2 @ hyunyoung2-desktop in ~/Labs/Konltk/Cython/7.Calling_external_C_function on git:master x [19:33:24] 
$ ./one_shot_compiling.sh 
the result of sum_function : 5
the result of substraction : 1

Let’s make extension module with these external c file and Cython.

First, you make the header file like .pxd file to include c code using into Cython module.

1
2
3
4
5
6
7
## In the c_function.pxd

cdef extern from "add.h":
    int sum_function(int a, int b)

cdef extern from "substraction.h":
    int substraction_function(int a, int b)

And Cython’s source file is .pyx file to wrap the c code to use from python.

1
2
3
4
5
6
7
8
9
10
11
## In the function.pyx
from c_function cimport sum_function, substraction_function
cimport cython

def sum(int a, int b):
    print("sum function is called!")
    return sum_function(a, b)

def substraction(int a, int b):
    print("substraction function is called!")
    return substraction_function(a, b)

For now, You can make .so file with .c and .pyx files.

Let’s see how to make it with setup.py

1
2
3
4
5
6
7
8
9
10
11
12
## In the setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

ext_modules = [Extension("operator",sources =["function.pyx", "add.c", "substraction.c"])]

setup(
    name="sum and substraction function",
    ext_modules = cythonize(ext_modules)
)

If you run the command like this:

python3 setup.py build_ext --inplace

1
2
3
4
5
6
7
8
9
10
11
# hyunyoung2 @ hyunyoung2-desktop in ~/Labs/Konltk/Cython/7.Calling_external_C_function on git:master x [20:23:32] 
$ ./run.sh 
Compiling function.pyx because it changed.
[1/1] Cythonizing function.pyx
running build_ext
building 'operator' extension
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I. -I/home/hyunyoung2/Labs/Konltk/Cython/env/include -I/usr/include/python3.5m -c function.c -o build/temp.linux-x86_64-3.5/function.o
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I. -I/home/hyunyoung2/Labs/Konltk/Cython/env/include -I/usr/include/python3.5m -c add.c -o build/temp.linux-x86_64-3.5/add.o
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I. -I/home/hyunyoung2/Labs/Konltk/Cython/env/include -I/usr/include/python3.5m -c substraction.c -o build/temp.linux-x86_64-3.5/substraction.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.5/function.o build/temp.linux-x86_64-3.5/add.o build/temp.linux-x86_64-3.5/substraction.o -o /home/hyunyoung2/Labs/Konltk/Cython/7.Calling_external_C_function/operator.cpython-35m-x86_64-linux-gnu.so
(env) 

And then run the python interpreter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# hyunyoung2 @ hyunyoung2-desktop in ~/Labs/Konltk/Cython/7.Calling_external_C_function on git:master x [20:23:34] 
$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import operator
>>> operator.
operator.__class__(         operator.__file__           operator.__init__(          operator.__new__(           operator.__sizeof__(        operator.sum(
operator.__delattr__(       operator.__format__(        operator.__le__(            operator.__package__        operator.__spec__           
operator.__dict__           operator.__ge__(            operator.__loader__         operator.__reduce__(        operator.__str__(           
operator.__dir__(           operator.__getattribute__(  operator.__lt__(            operator.__reduce_ex__(     operator.__subclasshook__(  
operator.__doc__            operator.__gt__(            operator.__name__           operator.__repr__(          operator.__test__           
operator.__eq__(            operator.__hash__(          operator.__ne__(            operator.__setattr__(       operator.substraction(      
>>> operator.sum(1,2)
sum function is called!
3
>>> operator.substraction(2,3)
substraction function is called!
1

Reference