I have an application written in C++ and a testing system (also in C++). Testing system is pretty complicated and hard to change (I want to make only small changes). My class looks like this:
class Derived : public Base {
public:
void somefunc(const AnotherClass& file) {
}
};
There are several functions inside. My testing system creates Derived class instance and then uses it's methods to do some stuff.
Now I want to be able to write a solution in Python. I need a two-way integration. My idea is to write Python function, which will be executed every time when somefunc
is called. And I don't want to lose variables' values in Python from one launch of function to another. And I also want to be able to use methods which are defined in the Base class instance from python. How can I achieve these things?
I chose Boost.Python for these purposes. For now, I understand, how to use c++ function, or even simple class in Python after some work. But I don't understand how to launch Python function from c++.
The second question - is Boost.Python a good choice? I need something very fast and, at the same time, easy to use.
Thank you for your help.
I would recommend using Cython for this sort of thing. Adapted examples from another question. (Edit: Upon request, I added an extended example that wraps a C++ class, see further below.)
Edit: Simple example, one way (C++ -> Python).
quacker.py:
def quack():
print("Quack!")
cquacker.pyx:
from quacker import quack
cdef public void cquack():
quack()
main.cpp:
#if _WIN32
#include <direct.h>
#define getcwd _getcwd
#define PATH_SEPARATOR ';'
#else
#include <unistd.h>
#define PATH_SEPARATOR ':'
#endif
#include <iostream>
#include <string>
#include <sstream>
#include <Python.h>
#include "cquacker.h"
std::wstring getSysPath()
{
char cwd[FILENAME_MAX];
getcwd(cwd, FILENAME_MAX);
std::wstringstream path;
path << Py_GetPath() << PATH_SEPARATOR << cwd;
return path.str();
}
int main()
{
Py_Initialize();
PySys_SetPath(getSysPath().c_str());
PyInit_cquacker();
if (PyErr_Occurred())
{
PyErr_Print();
return -1;
}
cquack();
Py_Finalize();
return 0;
}
Edit: Extended example, round trip (C++ -> Python -> C++).
quacker.py:
def qcallback(duck):
duck.quack()
quacker/Duck.hpp
#include <iostream>
namespace quacker {
class Duck
{
public:
void quack() { std::cout << "Quack!" << "\n"; }
};
}
cquacker_defs.pxd:
cdef extern from "quacker/Duck.hpp" namespace "quacker":
cdef cppclass Duck:
Duck() except +
void quack()
cquacker.pyx:
from cython.operator cimport dereference as deref
from libcpp.memory cimport shared_ptr
cimport cquacker_defs
from quacker import qcallback
cdef class Duck:
cdef shared_ptr[cquacker_defs.Duck] _this
@staticmethod
cdef inline Duck _from_this(shared_ptr[cquacker_defs.Duck] _this):
cdef Duck result = Duck.__new__(Duck)
result._this = _this
return result
def __init__(self):
self._this.reset(new cquacker_defs.Duck())
def quack(self):
assert self._this != NULL
deref(self._this).quack()
cdef public void cqcallback(shared_ptr[cquacker_defs.Duck] duck):
qcallback(Duck._from_this(duck))
main.cpp:
#if _WIN32
#include <direct.h>
#define getcwd _getcwd
#define PATH_SEPARATOR ';'
#else
#include <unistd.h>
#define PATH_SEPARATOR ':'
#endif
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include "quacker/Duck.hpp"
#include <Python.h>
#include "cquacker.h"
std::wstring getSysPath()
{
char cwd[FILENAME_MAX];
getcwd(cwd, FILENAME_MAX);
std::wstringstream path;
path << Py_GetPath() << PATH_SEPARATOR << cwd;
return path.str();
}
int main()
{
Py_Initialize();
PySys_SetPath(getSysPath().c_str());
PyInit_cquacker();
if (PyErr_Occurred())
{
PyErr_Print();
return -1;
}
auto duck = std::make_shared<quacker::Duck>();
cqcallback(duck);
Py_Finalize();
return 0;
}