Пример программы Cython как Конвертер Python в C

Я нашел здесь и здесь, что можно использовать Cython для преобразования Python в C, но я не могу найти ни одного пошагового примера. Допустим, у меня есть простая функция:

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

затем я запускаю: python setup.py build_ext --inplace, чтобы получить файлы foo.c и foo.so (и каталог для сборки). Ну, я хотел бы использовать переведенную (я надеюсь) функцию foo в main.c. Что я должен поместить в файл main.c и как его скомпилировать, чтобы можно было использовать функцию foo? Я использую GCC.

1 ответ

Решение

Далеко не эксперт AC, но для меня, использующего Ubuntu, работает следующее:

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

Из того же каталога:

$ 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

я использовал pkg-config --cflags python чтобы получить флаги:

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

Без вызова Py_Initialize (инициализируйте интерпретатор Python. В приложении, встраивающем Python, его следует вызывать перед использованием любых других функций API Python / C;), вы получите:

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

Без initfoo() или же import_foo() Вы получаете:

 Segmentation fault (core dumped)

Если вы не вызываете Py_Finalize:

Py_Initialize no-op при повторном вызове (без вызова Py_Finalize() первым).

Чтобы получить delorean пример из документации для запуска:

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

Процедура та же самая, единственное изменение, которое я должен был использовать ctypedef со структурой транспортного средства или в основном или использовать я не использовал struct Vehicle car; в основном:

$ 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

Вы также можете заставить его работать без использования Py_Initialize так далее...

В foo.pyx вам просто нужно сделать функцию публичной:

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

я добавил #include <python2.7/Python.h> только что импортированный foo.hв main.c и убрал Py_Initialize(); и т. д. просто импорт python.h не будет работать для меня, но это не может иметь место для всех.

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

Компиляция была такой же:

$ 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

Если вы используете версию api, просто включите заголовок api или наоборот согласно документам. Однако обратите внимание, что вы должны включить либо modulename.h, либо modulename_api.h в данный файл C, а не оба, иначе вы можете получить конфликтующий двойной определения.

Чтобы сделать то же самое с делорейским примером, я должен был использовать libc.stdio чтобы распечатать строки, чтобы избежать ошибки сегментации:

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

главный:

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

Возможно, имеет смысл возвращать значения:

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"

главный:

#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;
}
Другие вопросы по тегам