Работая над проектом на C++, я искал стороннюю библиотеку для чего-то, что не является моим основным бизнесом. Я нашел действительно хорошую библиотеку, делающую именно то, что нужно, но написанную на Python. Я решил поэкспериментировать со встраиванием кода Python в C++, используя библиотеку Boost.Python.
Код C++ выглядит примерно так:
#include <string>
#include <iostream>
#include <boost/python.hpp>
using namespace boost::python;
int main(int, char **)
{
Py_Initialize();
try
{
object module((handle<>(borrowed(PyImport_AddModule("__main__")))));
object name_space = module.attr("__dict__");
object ignored = exec("from myModule import MyFunc\n"
"MyFunc(\"some_arg\")\n",
name_space);
std::string res = extract<std::string>(name_space["result"]);
}
catch (error_already_set)
{
PyErr_Print();
}
Py_Finalize();
return 0;
}
(Очень) упрощенная версия кода Python выглядит так:
import thirdparty
def MyFunc(some_arg):
result = thirdparty.go()
print result
Теперь проблема в следующем: «MyFunc» работает нормально, я вижу печать «результата». Что я не могу сделать, так это прочитать «результат» из кода C++. Команда извлечения никогда не находит «результат» ни в одном пространстве имен. Я попытался определить «результат» как глобальный, я даже попытался вернуть кортеж, но не могу заставить его работать.





Вы должны иметь возможность вернуть результат из MyFunc, который затем окажется в переменной, которую вы в настоящее время называете «игнорируемой». Это избавляет от необходимости обращаться к нему каким-либо другим способом.
Я думаю, что вам нужен либо PyObject_CallObject(<py function>, <args>), который возвращает возвращаемое значение функции, которую вы вызываете как PyObject, либо PyRun_String(<expression>, Py_eval_input, <globals>, <locals>), который оценивает одно выражение и возвращает его результат.
Прежде всего, измените вашу функцию на return значение. print его усложнит, так как вы хотите вернуть ценность. Предположим, ваш MyModule.py выглядит так:
import thirdparty
def MyFunc(some_arg):
result = thirdparty.go()
return result
Теперь, чтобы делать то, что вы хотите, вам нужно выйти за рамки простого встраивания, например документация говорит. Вот полный код для запуска вашей функции:
#include <Python.h>
int
main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pFunc;
PyObject *pArgs, *pArg, *pResult;
int i;
Py_Initialize();
pName = PyString_FromString("MyModule.py");
/* Error checking of pName left out as exercise */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, "MyFunc");
/* pFunc is a new reference */
if (pFunc) {
pArgs = PyTuple_New(0);
pArg = PyString_FromString("some parameter")
/* pArg reference stolen here: */
PyTuple_SetItem(pArgs, 0, pArg);
pResult = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pResult != NULL) {
printf("Result of call: %s\n", PyString_AsString(pResult));
Py_DECREF(pResult);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr,"Call failed\n");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function");
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load module");
return 1;
}
Py_Finalize();
return 0;
}
Гораздо более исчерпывающий ответ, чем мой, от (я полагаю) другого родителя :) nosklo, я предлагаю вам расширить свой ответ примером PyRun_String; это дает больше гибкости.
Я думаю, ваш pArgs = PyTuple_New(0); должен сдавать 1, а не 0.
Кажется, я не могу получить свой код на C++, чтобы найти скрипт python. У меня скрипт находится в той же папке, что и .sln приложения С ++. PModule всегда равен NULL. Что я здесь делаю не так: stackoverflow.com/questions/55449475/…
Основываясь на ответах ΩΤΖΙΟΥ, Джоша и Носкло, я наконец-то получил работу с помощью boost.python:
Python:
import thirdparty
def MyFunc(some_arg):
result = thirdparty.go()
return result
C++:
#include <string>
#include <iostream>
#include <boost/python.hpp>
using namespace boost::python;
int main(int, char **)
{
Py_Initialize();
try
{
object module = import("__main__");
object name_space = module.attr("__dict__");
exec_file("MyModule.py", name_space, name_space);
object MyFunc = name_space["MyFunc"];
object result = MyFunc("some_args");
// result is a dictionary
std::string val = extract<std::string>(result["val"]);
}
catch (error_already_set)
{
PyErr_Print();
}
Py_Finalize();
return 0;
}
Некоторые важные моменты:
Независимо от того, что возвращается из MyFunc, я получаю: TypeError: объект 'NoneType' не поддерживает назначение элементов