2015-12-27 9 views
11

Ho trovato here e here che è possibile utilizzare Cython per convertire Python in C, ma non riesco a trovare alcun esempio passo-passo. Diciamo che ho una semplice funzione:Programma di esempio di Cython come Python to C Converter

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") 
) 

poi corro: python setup.py build_ext --inplace per ottenere i file foo.c e foo.so (e creare la directory). Bene, vorrei usare la funzione tradotta (spero) in main.c. Cosa devo inserire nel file main.c e come compilarlo per poter utilizzare la funzione foo? Sto usando gcc.

+0

Ho provato ** gcc -L. -Wall -o main main.c -lpython2.7 -l: foo.so **, ma penso che manchi qualcosa nel file main.c, perché ottengo "riferimento indefinito a' foo '" – Bociek

+0

Si desidera chiamare pippo da main.c? come http://docs.cython.org/src/userguide/external_C_code.html#c-api-declarations? –

+0

@PadraicCunnigham Precisamente! Voglio foo (x) call. – Bociek

risposta

12

Lontano da esperto ac ma per me usare Ubuntu, le seguenti opere:

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 

Dalla stessa directory:

$ cython foo.pyx 

Il n:

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

Quindi basta eseguire.

$ ./foo 
0.000000 
2.000000 
3.000000 
4.000000 
5.000000 

ho usato pkg-config --cflags python per ottenere le bandiere:

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

Senza rimettere Py_Initialize (inizializzare l'interprete Python In un'applicazione che contiene Python, questo dovrebbe essere chiamato prima di utilizzare qualsiasi altra API Python/C. funzioni;), si otterrà:

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

Senza initfoo() o import_foo() si ottiene un:

Segmentation fault (core dumped) 

Se non chiami Py_Finalize:

Py_Initializeun no-op quando viene chiamato per una seconda volta (senza chiamare Py_Finalize() prima).

Per ottenere l'esempio delorean dalla documentazione per eseguire:

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.pisside:

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") 

La procedura è la stessa, l'unico cambiamento è stato che ho dovuto usare ctypedef con la struct veicolo oppure in principale o utilizzare avevo t utilizzare struct Vehicle car; nel principale:

$ 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 

È può anche farlo funzionare senza usare Py_Initialize ecc ...

In foo.pyx solo bisogno di rendere la funzione pubblica:

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

ho aggiunto #include <python2.7/Python.h> appena importato foo.h in main.ce rimossi Py_Initialize(); ecc Basta importare python.h non avrebbe funzionato per me, ma che non può essere il caso per tutti.

#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; 
} 

Compilazione era la stessa:

$ 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 

Se si utilizza la versione di api poi basta includere l'intestazione api o viceversa secondo la documentazione Si noti tuttavia che si dovrebbe includere sia modulename. h o modulename_api.h in un dato file C, non entrambi, altrimenti potresti ottenere definizioni doppie in conflitto.

Per fare la stessa cosa con l'esempio DeLorean ho dovuto usare libc.stdio per stampare le stringhe per evitare un errore di segmentazione:

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") 

principale:

#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; 
} 

Potrebbe avere più senso restituire i valori:

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" 

principale:

#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; 
} 
+0

Grazie per la tua risposta @PadraicCunningham. Ho pensato di aggirare questo oggetto Py_Initialize() ecc. Esattamente come nell'esempio * delorean *. – Bociek

+0

@Bociek, ho aggiunto come farlo senza Py_Initialize(), ecc. –

+1

Nice @PadraicCunningham :) – Bociek