Вызов функции Cython из кода C вызывает ошибку сегментации - PullRequest
5 голосов
/ 12 апреля 2019

Я пытаюсь вызвать функцию cython (cdef) в C-программе. Когда функция cdef содержит операторы python, например, Функции print (0.5) или python (def), вызывающие функцию (cdef), вызывают ошибку сегментации.

Файл .pyx:

# cython: language_level=3

cdef public double PI = 3.1415926

cdef public double get_e():
    print("calling get_e()")
    return 2.718281828

Файл .c:

#include "Python.h"
#include "transcendentals.h"
#include <math.h>
#include <stdio.h>

int main(int argc, char **argv) {
  Py_Initialize();
  PyInit_transcendentals();
  printf("pi**e: %f\n", pow(PI, get_e()));
  Py_Finalize();
  return 0;
}

Команды компиляции:

cython transcendentals.pyx

gcc -I. -I/usr/include/python3.5m -I/usr/include/python3.5m \
-Wno-unused-result -Wsign-compare \
-g -fstack-protector-strong -Wformat \
-Werror=format-security -DNDEBUG -g \
-fwrapv -O3 -Wall -Wstrict-prototypes \
-L/usr/lib/python3.5/config-3.5m-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c \
-lpython3.5m -lpthread -ldl -lutil -lm -Xlinker \
-export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

Когда я удаляю оператор print функции get_e, ошибка сегментации не возникает. Но значение PI будет 0.

Ответы [ 2 ]

2 голосов
/ 13 апреля 2019

Полагаю, вы используете Cython 0.29. Начиная с 0.29 , PEP-489 инициализация многофазного модуля включена для версий Python> = 3.5.Это означает, что использование PyInit_XXX уже не достаточно, поскольку вы испытываете.

Документация Cython предлагает использовать механизм inittab , то есть вашу main -функциюдолжен выглядеть примерно так:

#include "Python.h"
#include "transcendentals.h"
#include <math.h>
#include <stdio.h>

int main(int argc, char **argv) {
  int status=PyImport_AppendInittab("transcendentals", PyInit_transcendentals);
  if(status==-1){
    return -1;//error
  } 
  Py_Initialize();
  PyObject *module = PyImport_ImportModule("transcendentals");

  if(module==NULL){
     Py_Finalize();
     return -1;//error
  }

  printf("pi**e: %f\n", pow(PI, get_e()));
  Py_Finalize();
  return 0;
}

Еще одна возможность восстановить старое поведение - определить макрос CYTHON_PEP489_MULTI_PHASE_INIT=0 и, таким образом, переопределить значение по умолчанию, например, передав -DCYTHON_PEP489_MULTI_PHASE_INIT=0 в gcc в командной строке.

2 голосов
/ 12 апреля 2019

Это похоже на ошибку (или, по крайней мере, проблему с Python3.7 ).

Я протестировал ваш пример на моем Arch Linux с Python3.7.

Первое, что меня заинтересовало, это то, сколько времени компиляция заняла этот шаг:

gcc -I. -I/usr/include/python3.7m -I/usr/include/python3.7m -Wno-unused-result \
-Wsign-compare -g -fstack-protector-strong -Wformat -Werror=format-security -g \
-fwrapv -O0 -Wall -Wstrict-prototypes -L/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c -lpython3.7m -lpthread -ldl -lutil -lm

У меня не такой уж плохой компьютер, но мне понадобилось несколько минут, чтобы завершить сборку. Странно.

И после запуска ./a.out я также получил ошибку сегментации, как и вы.


Итак, я решил протестировать (с одной незначительной модификацией: изменить PyInit_transcendentals на inittranscendentals в main) с Python2.7, как показано ниже:

gcc -I. -I/usr/include/python2.7 -I/usr/include/python2.7 -Wno-unused-result \
-Wsign-compare -g -fstack-protector-strong -Wformat -Werror=format-security \
-g -fwrapv -O0 -Wall -Wstrict-prototypes -L/usr/lib/python2.7/config-2.7-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c -lpython2.7 -lpthread -ldl -lutil -lm

Компиляция была мгновенной.

Я запустил ./a.out и вывел:

называется get_e (): 2.718282 вызов get_e ()
пи ** е: 22,459157


Тогда просто чтобы быть уверенным, что это не имеет никакого отношения к каким-либо флагам, которые вы могли бы использовать, а также к тому, что математическая библиотека или что-то еще будет здесь влиять, я повторил тест с очень простым "привет миром" "пример, как показано ниже.

  • main.c
#include <Python.h>
#include "hello.h"

int main() {
  Py_Initialize();
  inithello();
  hello();
  Py_Finalize();
  return 0;
}
  • hello.c
# cython: language_level=2

cdef public hello():
    print "hello!"

Тогда

cython hello.pyx
cc -c *.c -I /usr/include/python2.7/
cc -L /usr/lib/python2.7/ -lpython2.7 -ldl *.o -o main
./main

Вывод был

Привет!

С другой стороны, повторная компиляция с Python3.7 (после изменения inithello на PyInit_hello) выдает следующий вывод:

cc -c *.c -I /usr/include/python3.7m/
cc -L /usr/lib/python3.7/ -lpython3.7m -ldl *.o -o main
./main

Ошибка сегментации (ядро сброшено)

...