Python, встроенный в CPP: как вернуть данные в CPP

Работая над проектом на 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++. Команда извлечения никогда не находит «результат» ни в одном пространстве имен. Я попытался определить «результат» как глобальный, я даже попытался вернуть кортеж, но не могу заставить его работать.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
14
0
8 494
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы должны иметь возможность вернуть результат из MyFunc, который затем окажется в переменной, которую вы в настоящее время называете «игнорируемой». Это избавляет от необходимости обращаться к нему каким-либо другим способом.

Независимо от того, что возвращается из MyFunc, я получаю: TypeError: объект 'NoneType' не поддерживает назначение элементов

yoav.aviram 19.10.2008 04:39

Я думаю, что вам нужен либо 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; это дает больше гибкости.

tzot 19.10.2008 12:58

Я думаю, ваш pArgs = PyTuple_New(0); должен сдавать 1, а не 0.

Björn Lindqvist 17.01.2014 20:02

Кажется, я не могу получить свой код на C++, чтобы найти скрипт python. У меня скрипт находится в той же папке, что и .sln приложения С ++. PModule всегда равен NULL. Что я здесь делаю не так: stackoverflow.com/questions/55449475/…

Kanmani 14.04.2019 14:13

Основываясь на ответах ΩΤΖΙΟΥ, Джоша и Носкло, я наконец-то получил работу с помощью 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;
}

Некоторые важные моменты:

  1. Я изменил "exec" на "exec_file" из удобство, он также работает с простой 'exec'.
  2. Основная причина неудачи в том, что я не передал "местное" name_sapce в 'exec' или 'exec_file' - это сейчас исправлено путем двукратной передачи name_space.
  3. Если функция python возвращает строки Юникода, они не конвертируемый в 'std :: string', поэтому я пришлось добавить суффикс ко всем строкам Python с .encode ('ASCII', 'игнорировать') '.

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