Search code examples
pythonc++boost-python

Get access to C++ class created in python from C++


How to do this

c++ -> Python -> c++
 ^               |
 |               |
 -----------------
  1. C++ app is hosting python.
  2. Python creates a class, which is actually a wrapping to c/c++ object
  3. How to get access from hosting c++ to c/c++ pointer of this object created by python?

Example with code:

Imagine I have a C++ class wrapped for python (e.g. using boost.python)

// foo.cpp
#include <boost/python.hpp>

struct Cat {
   Cat (int fur=4): _fur{i} { }
   int get_fur() const { return _fur; }
private:
   int _fur;
};

BOOST_PYTHON_MODULE(foo) {
using namespace boost::python;

class_<Cat>("Cat", init<int>())
   .def("get_fur", &A::get_fur, "Density of cat's fur");

}

Now I'm hosting a python in C++. A python script creates Cat => A c++ Cat instance is created underneath. How to get a pointer to c++ instance of Cat from hosting c++ (from C++ to C++)?

#include <Python.h>

int
main(int argc, char *argv[])
{
  Py_SetProgramName(argv[0]);  /* optional but recommended */
  Py_Initialize();
  PyRun_SimpleString("from cat import Cat \n"
                     "cat = Cat(10) \n");

  // WHAT to do here to get pointer to C++ instance of Cat
   ... ??? ...

  std::cout << "Cat's fur: " << cat->get_fur() << std::endl

  Py_Finalize();
  return 0;
}

Real application The real problem is this: we have a c++ framework which has pretty complex initialization and configuration phase where performance is not critical; and then processing phase, where performance is everything. There is a python wrapping for the framework. Defining things in python is very convenient but running from python is still slower than pure c++ code. It is tempting for many reasons to do this configuration/initialization phase in python, get pointers to underneath C++ objects and then run in "pure c++ mode". It would be easy if we could write everything from scratch, but we have already pretty much defined (30 years old) c++ framework.


Solution

  • #include <Python.h>
    
    int main(int argc, char *argv[])
    {
      Py_SetProgramName(argv[0]);
      Py_Initialize();
    
      object module = import("__main__");
      object space = module.attr("__dict__");
    
      exec("from cat import Cat \n"
           "cat = Cat(10) \n",
           space);
    
      Cat& cat = extract<Cat&>(space["cat"]);
      std::cout<<"Cat's fur: "<< cat.get_fur() <<"\n";
    
      //or:
    
      Cat& cat2 = extract<Cat&>(space.attr("cat"));
      std::cout<<"Cat's fur: "<< cat2.get_fur() <<"\n";
    
      //or:
    
      object result = eval("cat = Cat(10)");
      Cat& cat3 = extract<Cat&>(result);
      std::cout<<"Cat's fur: "<< cat3.get_fur() <<"\n";
    
      Py_Finalize();
      return 0;
    }