Pergunta

Enquanto trabalhava em um projeto C ++, eu estava olhando para uma biblioteca de terceiros para algo que não é o meu core business. Eu encontrei um muito bom biblioteca, fazendo exatamente o que é necessário, mas é escrito em Python. Eu decidi experimentar com incorporação de código Python em C ++, usando a biblioteca Boost.Python.

A C ++ olhares de código algo como isto:

#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;
}

A (muito) versão simplificada dos olhares código Python como este:

import thirdparty

def MyFunc(some_arg):
    result = thirdparty.go()
    print result

Agora, o problema é o seguinte: executa 'myfunc' bem, eu posso ver a impressão de 'resultado'. O que eu não posso fazer é ler de volta 'resultado' do código C ++. O comando extrato nunca encontra 'resultado' em qualquer namespace. Tentei definir 'resultado' como um global, eu até tentei retornando uma tupla, mas eu não posso obtê-lo para o trabalho.

Foi útil?

Solução

Em primeiro lugar, mudar a sua função para return o valor. printing que vai complicar as coisas desde que você deseja obter o valor de volta. Suponha que a sua aparência MyModule.py assim:

import thirdparty

def MyFunc(some_arg):
    result = thirdparty.go()
    return result

Agora, para fazer o que você quer, você tem de ir além incorporação básico, como a documentação diz . Aqui está o código completo para executar a função:

#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;
}

Outras dicas

Com base ??O?????, Josh e respostas de Nosklo eu finalmente consegui-lo funcionar usando 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;
}

Alguns pontos importantes:

  1. Eu mudei 'exec' para 'exec_file' fora de conveniência, ele também trabalha com plain 'exec'.
  2. A principal razão que falhou é que i não passou um name_sapce "local" para 'Exec' ou 'exec_file' - este é agora fixo, passando name_space duas vezes.
  3. Se a função python retornos strings unicode, eles não são conversível para 'std :: string', então eu teve de sufixo todas as cordas python com '.encode ( 'ASCII', 'ignorar')'.

Eu acho que o que você precisa é de PyObject_CallObject(<py function>, <args>), que retorna o valor de retorno da função que você chama como um PyObject, ou PyRun_String(<expression>, Py_eval_input, <globals>, <locals>) que avalia uma única expressão e retorna seu resultado.

Você deve ser capaz de retornar o resultado de MyFunc, que depois acabam na variável que você está chamando de "ignorado". Isso elimina a necessidade de acessá-lo de qualquer outra forma.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top