2015-09-16 4 views
1

Я пытаюсь встроить скрипт Python в свою программу на C++. После прочтения некоторых вещей о внедрении и расширении я понимаю, как открыть свой собственный скрипт python и как передать некоторые целые числа. Но теперь я в какой-то момент не понимаю, как решить мою проблему. Я должен сделать оба, вызывая функции Python из C++ и вызывая функции C++ из моего встроенного сценария Python. Но я не знаю, с чего начать. Я знаю, что мне нужно скомпилировать файл .so, чтобы разоблачить мои функции C++ для Python, но это ничего не могу сделать, потому что мне нужно встроить свой файл Python и управлять им с помощью кода на C++ (мне нужно расширить большое программное обеспечение с помощью язык скриптов, чтобы сделать некоторую логику легко отредактированной).Вложение Python в C++ и методы вызова из кода C++

Итак, есть ли способ сделать обе вещи? Вызов функций Python из C++ и вызов функций C++ из Python?

Это мой C++ кода

#include <Python.h> 
#include <boost/python.hpp> 
using namespace boost::python; 


// <----------I want to use this struct in my python file--------- 
struct World 
{ 
    void set(std::string msg) { this->msg = msg; } 
    std::string greet() { return msg; } 
    std::string msg; 
}; 


// Exposing the function like its explained in the boost.python manual 
// but this needs to be compiled to a .so to be read from the multiply.py 
BOOST_PYTHON_MODULE(hello) 
{ 
    class_<World>("World") 
     .def("greet", &World::greet) 
     .def("set", &World::set) 
    ; 
} 
// <--------------------------------------------------------------- 


int 
main(int argc, char *argv[]) // in the main function is only code for embedding the python file, its not relevant to this question 
{ 
    setenv("PYTHONPATH",".",1); 
    PyObject *pName, *pModule, *pDict, *pFunc; 
    PyObject *pArgs, *pValue; 
    int i; 

    if (argc < 3) { 
     fprintf(stderr,"Usage: call pythonfile funcname [args]\n"); 
     return 1; 
    } 

    Py_Initialize(); 
    pName = PyString_FromString(argv[1]); 
    /* Error checking of pName left out */ 

    pModule = PyImport_Import(pName); 
    Py_DECREF(pName); 

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, argv[2]); 
     /* pFunc is a new reference */ 

     if (pFunc && PyCallable_Check(pFunc)) { 
      pArgs = PyTuple_New(argc - 3); 
      for (i = 0; i < argc - 3; ++i) { 
       pValue = PyInt_FromLong(atoi(argv[i + 3])); 
       if (!pValue) { 
        Py_DECREF(pArgs); 
        Py_DECREF(pModule); 
        fprintf(stderr, "Cannot convert argument\n"); 
        return 1; 
       } 
       /* pValue reference stolen here: */ 
       PyTuple_SetItem(pArgs, i, pValue); 
      } 
      pValue = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pValue != NULL) { 
       printf("Result of call: %ld\n", PyInt_AsLong(pValue)); 
       Py_DECREF(pValue); 
      } 
      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 \"%s\"\n", argv[2]); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load \"%s\"\n", argv[1]); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 

и это мой файл Python

import hello_ext #importing the C++ file works only if its compiled as a .so 
planet = hello.World() #this class should be exposed to python 
planet.set('foo') 

def multiply(a,b): 
    planet.greet() 
    print "Will compute", a, "times", b 
    c = 0 
    for i in range(0, a): 
     c = c + b 
    return c 
+0

Если это не ясно, я ищу что-то вроде 'lua_register (lua_State * L, имя строки, функция func)' для python. – Ventu

ответ

6

Короче говоря, расширение Python, которые статически связаны со встроенным Python должен иметь их функциональный модуль инициализатор явно добавила в таблицу инициализации до инициализации интерпретатора.

PyImport_AppendInittab("hello", &inithello); 
Py_Initialize(); 

Boost.Python использует BOOST_PYTHON_MODULE макрос для определения модуля Python инициализатор. Результирующая функция не является импортером модуля. Это отличие похоже на создание модуля example.py и вызов import example.

При импорте модуля Python сначала проверит, является ли модуль встроенным модулем. Если модуля там нет, тогда Python будет искать module search path, пытаясь найти файл или библиотеку python на основе имени модуля. Если библиотека найдена, то Python ожидает, что библиотека предоставит функцию, которая будет инициализировать модуль. После обнаружения импорт создаст пустой модуль в таблице модулей, а затем инициализирует его. Для статически связанных модулей, таких как hello в исходном коде, путь поиска модуля не будет полезен, так как нет библиотеки для его поиска.

Для внедрения в документацию module table and initialization function указано, что для статических модулей функция инициализатора модуля не будет автоматически вызвана, если в таблице инициализации не будет записи. Для Python 2 и Python 3, можно сделать это по телефону PyImport_AppendInittab() перед тем Py_Initialize():

BOOST_PYTHON_MODULE(hello) 
{ 
    // ... 
} 

PyImport_AppendInittab("hello", &inithello); 
Py_Initialize(); 
// ... 
boost::python::object hello = boost::python::import("hello"); 

Также обратите внимание, что в языке Python C API для встраивания измененного именования для функций инициализации модуля между Python 2 и 3, поэтому для BOOST_PYTHON_MODULE(hello), один, возможно, придется использовать &inithello для Python 2 и &PyInit_hello для Python 3.


Вот полный пример demonstrating с внедренной импортировать Python модуль с demo пользователя, который будет затем импортировать в tatically связанный модуль hello. Он также вызывает функцию в пользовательском модуле demo.multiply, который затем вызывает метод, открытый через статически связанный модуль.

#include <cstdlib> // setenv, atoi 
#include <iostream> // cerr, cout, endl 
#include <boost/python.hpp> 

struct World 
{ 
    void set(std::string msg) { this->msg = msg; } 
    std::string greet()  { return msg;  } 
    std::string msg; 
}; 

/// Staticly linking a Python extension for embedded Python. 
BOOST_PYTHON_MODULE(hello) 
{ 
    namespace python = boost::python; 
    python::class_<World>("World") 
    .def("greet", &World::greet) 
    .def("set", &World::set) 
    ; 
} 

int main(int argc, char *argv[]) 
{ 
    if (argc < 3) 
    { 
    std::cerr << "Usage: call pythonfile funcname [args]" << std::endl; 
    return 1; 
    } 
    char* module_name = argv[1]; 
    char* function_name = argv[2]; 

    // Explicitly add initializers for staticly linked modules. 
    PyImport_AppendInittab("hello", &inithello); 

    // Initialize Python. 
    setenv("PYTHONPATH", ".", 1); 
    Py_Initialize(); 

    namespace python = boost::python; 
    try 
    { 
    // Convert remaining args into a Python list of integers. 
    python::list args; 
    for (int i=3; i < argc; ++i) 
    { 
     args.append(std::atoi(argv[i])); 
    } 

    // Import the user requested module. 
    // >>> import module 
    python::object module = python::import(module_name); 

    // Invoke the user requested function with the provided arguments. 
    // >>> result = module.fn(*args) 
    python::object result = module.attr(function_name)(*python::tuple(args)); 

    // Print the result. 
    std::cout << python::extract<int>(result)() << std::endl; 
    } 
    catch (const python::error_already_set&) 
    { 
    PyErr_Print(); 
    return 1; 
    } 

    // Do not call Py_Finalize() with Boost.Python. 
} 

Содержание demo.py:

import hello 
planet = hello.World() 
planet.set('foo') 

def multiply(a,b): 
    print planet.greet() 
    print "Will compute", a, "times", b 
    c = 0 
    for i in range(0, a): 
     c = c + b 
    return c 

Использование:

$ ./a.out demo multiply 21 2 
foo 
Will compute 21 times 2 
42 

В приведенном выше коде, я решил использовать Boost.Python вместо Python/C API, с C++ комментарии аннотируются с эквивалентным кодом Python. Я считаю, что это намного более кратким и гораздо менее подверженным ошибкам. Если возникает ошибка Python, Boost.Python выдаст исключение, и все подсчет ссылок будут обработаны соответствующим образом.

Кроме того, при использовании Boost.Python не вызывайте Py_Finalize(). Согласно Embedding - Getting started секции:

Следует отметить, что в это время вы не должны вызывать Py_Finalize() остановить переводчик. Это может быть исправлено в будущей версии boost.python.

Смежные вопросы