Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Example program of Cython as Python to C Converter

Tags:

python

c

cython

I found here and here that one can use Cython to convert Python to C, but I cannot find any step-by-step example. Let's say I have a simple function:

foo.pyx

cdef void foo(double* x):
   x[0] = 0.0

setup.py

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

setup(
    ext_modules = cythonize("foo.pyx")
)

then I run: python setup.py build_ext --inplace to get foo.c and foo.so files (and build directory). Well, I would like to use translated (I hope) foo function in main.c. What should I put into main.c file and how to compile it in order to be able to use foo function? I am using gcc.

like image 924
Bociek Avatar asked Dec 27 '15 13:12

Bociek


1 Answers

Far from a c expert but for me using ubuntu, the following works:

main.c:

#include "foo_api.h"
#include <stdio.h>


int main(int argc, char *argv[]) {
     Py_Initialize();
     initfoo();
     import_foo();
     double arr[5] = {1,2,3,4,5};
     int i = 0;
     foo(arr);
     for(i = 0; i < 5; i++)
    {
      printf("%f\n", arr[i]);
    }
     Py_Finalize();
     return 0;
}

foo.pyx:

cdef public api  foo(double* x):
   x[0] = 0.0

From the same directory:

$ cython foo.pyx 

Then:

$ cc -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o foo  *.c -lpython2.7 

Then just run.

$ ./foo
0.000000
2.000000
3.000000
4.000000
5.000000

I used pkg-config --cflags python to get the flags:

 $ pkg-config --cflags python
-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7 

Without calling Py_Initialize (Initialize the Python interpreter. In an application embedding Python, this should be called before using any other Python/C API functions;), you will get:

Fatal Python error: PyThreadState_Get: no current thread
Aborted (core dumped)

Without initfoo() or import_foo() you get a:

 Segmentation fault (core dumped)

If you don't call Py_Finalize:

Py_Initialize a no-op when called for a second time (without calling Py_Finalize() first).

To get the delorean example from the docs to run:

main.py:

#include "delorean_api.h"
#include <stdio.h>
Vehicle car;


int main(int argc, char *argv[]) {
     Py_Initialize();
     initdelorean();
     import_delorean();
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     activate(&car);
     Py_Finalize();
     return 0;
}

delorean.pyx:

ctypedef public struct Vehicle:
    int speed
    float power

cdef api void activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        print "Time travel achieved"
    else:
        print("Sorry Marty")

The procedure is the same, the only change was I had to use ctypedef with the Vehicle struct or else in main or use I had t use struct Vehicle car; in main:

$ cython delorean.pyx
$ cc  -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o delorean  *.c -lpython2.7  
$ ./delorean 1 1
Sorry Marty
$ ./delorean 100 2
Time travel achieved

You can also get it to work without using Py_Initialize etc...

In foo.pyx you just need to make the function public:

cdef public  foo(double* x):
   x[0] = 0.0

I added #include <python2.7/Python.h> just imported foo.hin main.c and removed Py_Initialize(); etc. Just importing python.h would not work for me but that may not be the case for everyone.

#include <python2.7/Python.h>
#include "foo.h"
#include <stdio.h>


int main(int argc, char *argv[]) {
     double arr[5] = {1,2,3,4,5};
     int i = 0;
     foo(arr);
     for(i = 0; i < 5; i++)
    {
      printf("%f\n", arr[i]);
    }

     return 0;
}

Compiling was the same:

$ cython foo.pyx 
$ cc -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o foo  *.c -lpython2.7 
$ ./foo
0.000000
2.000000
3.000000
4.000000
5.000000

If you are using the api version then just include the api header or vice versa as per the docs However, note that you should include either modulename.h or modulename_api.h in a given C file, not both, otherwise you may get conflicting dual definitions.

To do the same with the delorean example I had to use libc.stdio to print the strings to avoid a segmentation fault:

from libc.stdio cimport printf

ctypedef public  struct Vehicle:
    int speed
    float power

cdef public void activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        printf("Time travel achieved\n")
    else:
        printf("Sorry Marty\n")

main:

#include <python2.7/Python.h>
#include <stdio.h>
#include "delorean.h"

Vehicle car;


int main(int argc, char *argv[]) {
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     activate(&car);
     return 0;
}

It might make more sense to return the values:

ctypedef public  struct Vehicle:
    int speed
    float power

cdef public  char* activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        return  "Time travel achieved"
    return "Sorry Marty"

main:

#include <python2.7/Python.h>
#include <stdio.h>
#include "delorean.h"

Vehicle car;

int main(int argc, char *argv[]) {
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     printf("%s\n",activate(&car));
     return 0;
}
like image 90
Padraic Cunningham Avatar answered Nov 09 '22 01:11

Padraic Cunningham