Моя цель - вызвать функции Python из С++. Эти функции Python должны быть скомпилированы с помощью cython в файле .so. Файл .so должен быть тем, который взаимодействует с программой C++.
Перед всеми:
Я на Ubuntu, я работаю с miniconda с python3.9. Я нахожусь в папке (~/exp), сделанной так:
Я перевожу main.py в main.pyx, файл содержит такой код:
def add(a, b):
return a+b
def entry_point():
print(add(21,21))
Я скомпилировал этот скрипт с помощью cython и получил файл .so с этим скриптом setup.py:
from setuptools import setup
from Cython.Build import cythonize
setup(
name='exp',
ext_modules=cythonize("exp/main.pyx"),
libraries=[('python3.9', {'include_dirs': ["~/miniconda3/include/python3.9"]})],
library_dirs=['~/miniconda3/lib']
)
И эта команда:
python3 setup.py build_ext --inplace
Теперь у меня есть файл main.cpython-39-x86_64-linux-gnu.so в ~/exp/exp.
Когда я запускаю (run.py), который содержит:
from exp.main import entry_point
if __name__ == "__main__":
entry_point()
У меня нормальное поведение: он возвращает 42.
А вот и проблемы
Я компилирую исходный код run.cpp, который содержит:
#include <iostream>
#include <dlfcn.h>
#include "Python.h"
int main(int argc, char *argv[]) {
setenv("PYTHONPATH",".",1);
Py_Initialize();
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyUnicode_FromString((char*)"exp/main");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"entry_point");
Py_DECREF(pValue);
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
Py_Finalize();
}
с командой:
g++ -Wall -I~/miniconda3/include/python3.9 run.cpp -L~/miniconda3/lib -lpython3.9 -o run.o -ldl
А затем выполните: ./run.o Чтобы закончить красиво:
Segmentation fault (core dumped)
Я пробовал с dlopen тоже безуспешно. Может я что-то упускаю, буду рада любой помощи.
Спасибо :)
Вы полностью пропускаете проверку ошибок в файле c++ — большинство функций Python возвращают NULL
, чтобы сообщить вам, произошло ли исключение.
И exp/main
не является допустимым путем к модулю
Во-первых, спасибо Craig Estey и DavidW за их комментарии.
Итак, я, наконец, смог заставить его работать, две вещи были неправильными:
Очень последняя вещь. Что-то, что я пропустил, было PyObject_CallObject
, которое позволяет вызывать мой PyObject pFunc
.
Наконец-то я получил свой ответ «42».
Вот окончательный код:
#include <iostream>
#include <dlfcn.h>
#include "Python.h"
int main(int argc, char *argv[]) {
setenv("PYTHONPATH",".",1);
Py_Initialize();
PyObject *pName, *pModule, *pDict, *pFunc, *presult;
// Initialize the Python Interpreter
// Build the name object
pName = PyUnicode_FromString((char*)"exp.main");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"entry_point");
presult = PyObject_CallObject(pFunc, NULL);
// Py_DECREF(pValue);
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
Py_Finalize();
}
(Крейг указал, что исполняемый файл может не заканчиваться на «.o», узнайте больше: Что такое файл *.o) Итак, новая команда компиляции:
g++ -Wall -I~/miniconda3/include/python3.9 run.cpp -L~/miniconda3/lib -lpython3.9 -o run
Вам не хватает какой-либо проверки ошибок
Да, спасибо, что напомнили, я забыл проверить ошибки. (Этот код не был окончательным кодом, который я буду использовать, но я все равно должен был его указать;))
Скомпилируйте
.cpp
с-g
, чтобы получить символы отладки. Затем выполните:gdb ./run.o
для запуска под отладчиком. Вам может понадобиться--rpath
вместо или в дополнение к:-L
. Примечание: хотя в данном случае это может и не помешать, но, как правило, исполняемые файлы не должны заканчиваться на.o
(это расширение для перемещаемых файлов). Я мог бы немного изменить некоторые вещи, чтобы.cpp
файл попал в-o run
Вы можете сделать:ldd ./run
чтобы увидеть, что.so
нужно исполняемому файлу (и он сообщит вам, может ли он их найти).