Run python in C++ [closed]

2019-06-26 06:27发布

问题:

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.

回答1:

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