Нам нужно добавить некоторое дополнительное состояние к обратному вызову, а именно ссылку на экземпляр класса, функцию-член которого мы хотим вызвать.
Для этого мы можем использовать класс.Чтобы сделать функциональность эквивалентной простому использованию статической функции обратного вызова, давайте определим operator()
(т.е. сделаем его функтором), а также предоставим этот оператор Python.
Предположим, у нас есть следующий класс приложения:
class app
{
public:
explicit app(std::string name) : name_(std::move(name)) {}
int run();
void callback(double t_prog, double t_final);
private:
std::string name_;
};
В run()
мы выполняем наш скрипт Python и хотим, чтобы он вызывал функцию-член callback
текущего экземпляра.
Давайте определим следующий класс обработчика обратного вызова:
class callback_handler
{
public:
explicit callback_handler(app& a) : app_(a) {}
void operator()(double t_prog, double t_final)
{
app_.callback(t_prog, t_final);
}
private:
app& app_;
};
Нам нужно представить этот класс Python, но мы не хотим иметь возможность создавать новые экземпляры из Python, и при этом мы не хотим, чтобы он копировался (хотя это не имеет большого значенияздесь, так как наше состояние состоит только из ссылок).
BOOST_PYTHON_MODULE(cbtest)
{
bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
.def("__call__", &callback_handler::operator())
;
};
В начале нашего приложения мы должны обязательно инициализировать наш модуль перед его использованием - вызовите initcbtest();
сразу после инициализацииИнтерпретатор Python.
Теперь мы можем использовать наш обработчик обратного вызова следующим образом (код Python остается неизменным, так как объект вызывается):
callback_handler cbh(*this);
bp::object result = MyFunc(1, 10, 2, boost::ref(cbh));
std::cout << "result = " << bp::extract<double>(result) << "\n";
Пример кода
#include <boost/noncopyable.hpp>
#include <boost/python.hpp>
#include <iostream>
// ============================================================================
namespace bp = boost::python;
// ============================================================================
class app
{
public:
explicit app(std::string name) : name_(std::move(name)) {}
int run();
void callback(double t_prog, double t_final);
private:
std::string name_;
};
// ============================================================================
class callback_handler
{
public:
explicit callback_handler(app& a) : app_(a) {}
void operator()(double t_prog, double t_final)
{
app_.callback(t_prog, t_final);
}
private:
app& app_;
};
// ----------------------------------------------------------------------------
BOOST_PYTHON_MODULE(cbtest)
{
bp::class_<callback_handler, boost::noncopyable>("callback_handler", bp::no_init)
.def("__call__", &callback_handler::operator())
;
};
// ============================================================================
void app::callback(double t_prog, double t_final)
{
std::cout << "CB(" << name_ << ") " << t_prog << " " << t_final << "\n";
}
// ----------------------------------------------------------------------------
int app::run()
{
Py_Initialize();
initcbtest();
try {
bp::object module = bp::import("__main__");
bp::object name_space = module.attr("__dict__");
bp::exec_file("MyModule.py", name_space, name_space);
bp::object MyFunc = name_space["MyFunc"];
callback_handler cbh(*this);
bp::object result = MyFunc(1, 10, 2, boost::ref(cbh));
std::cout << "result = " << bp::extract<double>(result) << "\n";
} catch (bp::error_already_set&) {
PyErr_Print();
}
Py_Finalize();
return 0;
}
// ============================================================================
int main()
{
app a("TestApp");
return a.run();
}
// ============================================================================
Python Script
Файл MyModule.py
:
def MyFunc(a, b, c, callback):
result = 0
for i in range(a, b, c):
result += i
callback(i, b)
return result
Консольный вывод
CB(TestApp) 0 10
CB(TestApp) 2 10
CB(TestApp) 4 10
CB(TestApp) 6 10
CB(TestApp) 8 10
result = 20