Вызов функции из сгенерированного cython файла .so внутри кода С++

Моя цель - вызвать функции Python из С++. Эти функции Python должны быть скомпилированы с помощью cython в файле .so. Файл .so должен быть тем, который взаимодействует с программой C++.

Перед всеми:

Я на Ubuntu, я работаю с miniconda с python3.9. Я нахожусь в папке (~/exp), сделанной так:

  • опыт
    • опыт
      • __ инициализировать __.py
      • main.py
  • setup.py
  • запустить.cpp
  • запустить.py

Я перевожу 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 тоже безуспешно. Может я что-то упускаю, буду рада любой помощи.

Спасибо :)

Скомпилируйте .cpp с -g, чтобы получить символы отладки. Затем выполните: gdb ./run.o для запуска под отладчиком. Вам может понадобиться --rpath вместо или в дополнение к: -L. Примечание: хотя в данном случае это может и не помешать, но, как правило, исполняемые файлы не должны заканчиваться на .o (это расширение для перемещаемых файлов). Я мог бы немного изменить некоторые вещи, чтобы .cpp файл попал в -o run Вы можете сделать: ldd ./run чтобы увидеть, что .so нужно исполняемому файлу (и он сообщит вам, может ли он их найти).

Craig Estey 08.02.2023 18:23

Вы полностью пропускаете проверку ошибок в файле c++ — большинство функций Python возвращают NULL, чтобы сообщить вам, произошло ли исключение.

DavidW 08.02.2023 19:09

И exp/main не является допустимым путем к модулю

DavidW 08.02.2023 19:11
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, спасибо Craig Estey и DavidW за их комментарии.

Итак, я, наконец, смог заставить его работать, две вещи были неправильными:

  1. pValue не использовался, поэтому Py_DECREF вызвал ошибку
  2. путь к модулю «exp/main» действительно недействителен, но «exp.main» действителен.

Очень последняя вещь. Что-то, что я пропустил, было 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

Вам не хватает какой-либо проверки ошибок

DavidW 10.02.2023 17:12

Да, спасибо, что напомнили, я забыл проверить ошибки. (Этот код не был окончательным кодом, который я буду использовать, но я все равно должен был его указать;))

Guillaume 13.02.2023 13:18

Другие вопросы по теме