6

The following example from Boost.Python v1.56 shows how to embed the Python 3.4.2 interpreter into your own application. Unfortunately that example does not work out of the box on my configuration with MSVC2013 under Windows 8.1. And I have not found 1 working complete example about embedding, at least none that is younger than 10 years or so.

I receive the following error running it: ImportError: 'embedded_hello' is not a built-in module

The code is here: http://pastebin.com/shTtdxT8

Any hints what I can do to let this run? And in general how to expose a c++ class in Python and vice versa?

1 Answer 1

11

The code is compiling with a Python 2 header configuration. When compiling with a Python 3 header configuration, the boost/python/module_init.hpp would have declared the embedded_hello module's initialization function as PyInit_embedded_hello rather than initembedded_hello. I highly recommend verifying the proper header configuration, and performing a clean build of Boost.Python, as Boost.Python and modules built with the library need to use the same header configuration.

Additionally, when adding modules to the built-in table, the PyImport_AppendInittab() calls need to occur before Py_Initialize(). The PyImport_AppendInittab() documentation explicitly states:

Add a single module to the existing table of built-in modules. ... This should be called before Py_Initialize().

Boost.Python uses the BOOST_PYTHON_MODULE macro to define a Python module. Within the body of the module, the current scope is the module itself. Thus, when C++ types are exposed via type wrappers, such as when a C++ classes is exposed to Python via boost::python::class_, the resulting Python class will be within the module defined by BOOST_PYTHON_MODULE.

On the other hand, user-defined types declared in Python are first-class objects. From a C++ perspective, they can be treated as though they are a factory function. Hence, to use a Python defined class in C++, one needs to get a handle to the class object, then instantiate an instance of a class by calling the class object.


Here is a complete minimal example demonstrating embedding a Python 3 interpreter that:

  • Imports a module (example) that has been built directly into the binary and exposes a basic C++ class (spam_wrap) to Python (example.Spam) that has virtual function/dispatching with a default.
  • Demonstrates using the exposed Python class (example.Spam).
  • Derives from the exposed Python class (example.Spam) within Python (example.PySpam) and uses the resulting class.
#include <iostream>
#include <boost/python.hpp>

/// @brief Mockup Spam model.
struct spam
  : boost::noncopyable
{
  virtual ~spam() {};
  virtual std::string hello() { return "Hello from C++"; }
};

//@ brief Mockup Spam wrapper.
struct spam_wrap
  : spam,
    boost::python::wrapper<spam>
{
  virtual std::string hello()
  {
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
    return boost::python::call<std::string>(
      this->get_override("hello").ptr());
#else
    return this->get_override("hello")();
#endif
  }

  std::string default_hello() { return this->spam::hello(); }
};

/// @brief Python example module.
BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  // Expose C++ spam_wrap as Python Spam class.
  python::class_<spam_wrap, boost::noncopyable>("Spam")
    .def("hello", &spam::hello, &spam_wrap::default_hello)
    ;
}   

int main()
{
  // Add example to built-in.
  PyImport_AppendInittab("example", &PyInit_example);

  // Start the interpreter.
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    python::object main = python::import("__main__");
    python::object global = main.attr("__dict__");

    // Execute Python code, using the example module.
    exec(
      "from example import Spam          \n"
      "spam = Spam()                     \n"
      "                                  \n"
      "class PySpam(Spam):               \n"
      "    def hello(self):              \n"
      "        return 'Hello from Python'\n",     
      global, global);

    /// Check the instance of the Python object using the C++ class.
    // >>> spam_object = spam
    python::object spam_object = global["spam"];
    assert(python::extract<spam>(spam_object).check());
    // >>> result = spam_object.hello()
    python::object result = spam_object.attr("hello")();
    // >>> print(result)
    std::cout << python::extract<std::string>(result)() << std::endl;
    // >>> assert("Hello from C++" == result)
    assert("Hello from C++" == python::extract<std::string>(result)());

    /// Create an instance using PySpam class.  It too is a Python object.
    // >>> py_spam_type = PySpam
    python::object py_spam_type = global["PySpam"];
    // >>> py_spam_object = py_spam_type()
    python::object py_spam_object = py_spam_type();
    // >>> result = py_spam_object()
    result = py_spam_object.attr("hello")();
    // >>> print(result)
    std::cout << python::extract<std::string>(result)() << std::endl;
    // >>> assert("Hello from Python" == result)
    assert("Hello from Python" == python::extract<std::string>(result)());
  }
  catch (const python::error_already_set&)
  {
    PyErr_Print();
  }
}

The program should run to completion without errors, resulting in the following output:

Hello from C++
Hello from Python
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for your example. Yes I forgot to change the PyInit_ for Python3 in the code on PasteBin, corrected it. Your example works so far for a simple class. I have to find out if it will also work for derived from abstract classes with virtual function and all that stuff. But now how can I get the class defined in Python back into C++? That part still doesn't work. :(
@patlecat Getting the module to load often tends to be the harder part. The rest is just writing Python in C++ using Boost.Python. I've updated the example to be a bit more complex, demonstrating how to use a class defined in Python within C++. The C++ comments annotated with >>> show the equivalent Python call.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.