LINUX.ORG.RU

boost::python Как получить указатель на python функцию и вызвать ее из C++ кода?

 , ,


1

2

Hi all!!!

  1. Как получить ссылку на python функцию и какой будет ее тип?
  2. Как ее вызвать из C++?
  3. И как получить ссылку на python объект?

Хотелось бы средствами boost::python.

Есть python скрипт в котором определана pyFunc() и строка strA:

import sys, os
sys.path.append(os.getcwd())
strA = "python string"
def pyFunc():
    print('python: pyFunc()')
    return True

Есть C++ код, который выполняет этот код и забирает из окружения python что то:

 /* pytest.cpp */
 /* build: g++ -std=c++1y -fPIC -I/usr/include/python3.4 -lboost_python -lpython3.4m pytest.cpp */
 /* bould: g++ -std=c++1y -fPIC -I/usr/include/python3.4 -lboost_python-py34 -lpython3.4m pytest.cpp */
#include <string>
#include <iostream>
#include <boost/python.hpp>
using namespace boost::python;

int main() {
  try {
    Py_Initialize();
    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    object pyScript = exec_file("test.py", main_namespace);

    /* Тут хотелось бы получить ссылку на python строку, а не копировать ее в C++ строку s: */
    std::string s = extract<std::string>(main_namespace["strA"]);
    /* можно так: s = extract<std::string>(main_module.attr("strA")); */

    std::cout << "strA in C++ space: "<< s << std::endl;

    /* Второй вариант, */
    /* тут тоже хочу иметь ссылку на d["strA"], не пойму как привезти тип к std::string&: */
    dict d = extract<dict>(main_module.attr("__dict__"));
    d["strA"] += " +changed in cpp";

    object pyScript1 = exec("pyFunc()\n" /* это вызов pyFunc() из python кода */
                            "strA += ' +changed in python'\n"
                            , main_namespace);

    /* Вот именно поэтому нужна ссылка на pyhon строку: */
    s = extract<std::string>(main_namespace["strA"]);

    std::cout << "strA in C++ space: "<< s << std::endl;


    /* ********************************************************** */
    /* И самый главный вопрос: как мне получить ссылку на pyFunc, */
    /* да еще и вызвать ее из C++ кода???  */
    /* ********************************************************** */

    
    /* Note that at this time you must not call Py_Finalize() to stop the interpreter. This may be fixed in a future version of boost.python. */
    /* http://www.boost.org/doc/libs/master/libs/python/doc/html/tutorial/tutorial/embedding.html */
    /* Py_Finalize(); */
  } catch(error_already_set const &) {
    if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError)) {
      /* handle ZeroDivisionError specially */
    } else {
      /* print all other errors to stderr */
      PyErr_Print();
    }
  }
}

Заранее благодарен за Ваши ответы.

UPD: С вызовом функции разобрался. Не пойму только какой тип. И остался вопрос со ссылкой на strA.

    auto fptr1 = d["pyFunc"];
    std::function<bool()> fptr2(d["pyFunc"]);
    std::cout << bool(fptr1()) << " " << fptr2() <<std::endl;

    std::cout << typeid(fptr1).name() << std::endl;

★★

Последнее исправление: samson (всего исправлений: 1)

UPD:
Что нужно указать в .def(init<>) вместо std::function<bool()>?

Модуль собирается, но не передается указатель на функцию из python в C++.

class eventPython : public eventBase {
 public:
  eventPython(std::function<bool()>);
 private:
  std::function<bool()> evFunc;
  virtual bool run() override;
};

BOOST_PYTHON_MODULE(modulename) {
  class_<eventPython>("qcEv")

    /* ??? какой тут нужно указать тип вместо std::function<bool()> ??? */
    .def( init<std::function<bool()>>(args("eventFunc")) );
}

вполняем скрипт:

from modulename import qcEv
def pyFunc():
    print('python: pyFunc()')
    return True
e = qcEv(pyFunc)

Результат:

$ python test.py 
Traceback (most recent call last):                                                               
  File "test.py", line 14, in <module>                                                           
    e = qcEv(pyFunc)                                                                             
Boost.Python.ArgumentError: Python argument types in                                             
    qcEv.__init__(qcEv, function)                                                                
did not match C++ signature:                                                                     
    __init__(_object*, std::function<bool ()> eventFunc)                                         
    __init__(_object*) 

В предыдущем примере, обратно к std::function<> преобразуется: std::function<bool()> fptr2(d["pyFunc"]);

samson ★★
() автор топика
Последнее исправление: samson (всего исправлений: 1)

Как я понял тип будет boot::python::object

в первом примере прокатило так:

    object fptr1 = d["pyFunc"];
    std::function<bool()> fptr2(fptr1);
    std::cout << bool(fptr1()) << " " << fptr2() <<std::endl;

во втором так:

class eventPython : public eventBase {
 public:
  eventPython(boost::python::object);
 private:
  std::function<bool()> evFunc;
  virtual bool run() override;
};

BOOST_PYTHON_MODULE(modulename) {
  class_<eventPython>("qcEv")

    /* в конструктор будет передан boost::python::object, 
       который мы преобразуем к std::function<>
       или еще там к чему, например к bool(*)() */
    .def( init<boost::python::object>(args("eventFunc")) );
}

samson ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.