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;
}
Answer from Padraic Cunningham on Stack OverflowVideos
- Yes, at its core this is what Cython does. But ...
- You don't need Cython, however, you do need
libpython. You may feel like it doesn't use that many Python features, but I think if you try this you'll find it's not true -- you won't be able to separate your program from its dependence onlibpythonwhile still using the Python language.
Another option is PyPy, specifically it's translation toolchain, NOT the PyPy Python interpreter. It lets you translate RPython, a subset of the Python language, into C. If you really aren't using many Python language features or libraries, this may work.
PyPy is mostly known as an alternative Python implementation, but it is also a set of tools for compiling dynamic languages into various forms. This is what allows the PyPy implementation of Python, written in (R)Python, to be compiled to machine code.
If C++ is available, Nuitka is a Python to C++ compiler that works for regular Python, not just RPython (which is what shedskin and PyPy use).
If C++ is available for that embedded platform, there is shed skin, it converts python into c++.
when converting the code from python to c (using Cython) it converts it to c code which can be compiled into a shared object.
in order to make it executable, you should add "--embed" to cython conversion command. this flag adds the 'main' function you need, so you could compile the c code into executable file.
please notice you'll need the python .so runtime libraries in order to run the exec.
Read the Cython documentation. This will also (hopefully) teach you what Cython is and what it isn't. Cython is for creating python extensions (not a general-purpose Python-to-C-compiler), which are shared objects/dlls. Dynamically loaded libraries don't have a main function like standalone programs, but compilers assume that they are ultimately linking an executable. You have to tell them otherwise via flags (-shared methinks, but again, refer to the Cython documentation) - or even better, don't compile yourself, use a setup.py for this (yet again, read the Cython documentation).
Check the documentation. It's not enough to do gcc x.c -o x.
This page explains compilation: http://docs.cython.org/src/reference/compilation.html
There's a lot more to it, but a direct answer is:
Compiling your .c files will vary depending on your operating system. Python documentation for writing extension modules should have some details for your system. Here we give an example on a Linux system:
$ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o yourmod.so yourmod.c
Of course in your situation it's going to be something closer to -I/usr/include/python3.4, or even $(pkg-config --libs --cflags python3). And you're not building with -shared, because you want an executable.
Shortest "this has to work" set of commands is:
cython3 --embed greet.py -o greet.c
gcc $(pkg-config --libs --cflags python3) greet.c -o greet
You need to install pkg-config if it's missing.
As @viraptor's answer shows you and as per my comment, your main problem is that you need to tell your C compiler (e.g. gcc) where to find the python headers required (pyconfig.h and Python.h). To do this, you need to pass a -I option to gcc.
The other answer suggests using pkg-config to add this to your command line. However, like you, with Ubuntu 14.04, cython3 and python3-dev installs, using this method leads the compiled program to exit with a segmentation fault for me.
So, I suggest you go back to basics. After
cython greet.py -o greet.c
Run the following command. It assumes that Python.h and friends are in the standard place (i.e. you've done a standard install of python3-dev)
gcc -I/usr/include/python3.4m -o greet greet.c -lpython3.4m
If that doesn't work - use find / -iname Python.h to find the location of the necessary files and alter the -I path accordingly.
In time, when you want to use cython on more complex programs, such as those that link to other C libraries, you'll need to learn about the other options you need to pass to gcc to get it to compile and link correctly. To get you going, though, the above should work (tested on Ubuntu 14.04 as per your spec)
P.S. I'm not sure why the pkg-config suggestion doesn't work - but for me it seems to add in an extra path to -I which breaks things.