C ++ class not recognized by Python 3 as module with Boost.Python Embedding

The following example from Boost.Python v1.56 shows how to embed the Python 3.4.2 interpreter into your own application. Unfortunately this example does not work in my configuration with MSVC2013 under Windows 8.1. And I haven't found 1 complete example of implementation, at least 10 years old or so.

I am getting the following error: ImportError: 'embedded_hello' is not an inline module

Code here: http://pastebin.com/shTtdxT8

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

+3


source to share


1 answer


The code is compiled with a Python 2 header configuration. When compiled with a Python 2 header config, boost/python/module_init.hpp

it would declare the module initialization function embedded_hello

as PyInit_embedded_hello

, not initembedded_hello

. I highly recommend checking the correct header configuration and doing a clean build of Boost.Python, as Boost.Python and modules built using the library should use the same header configuration.

Also, when adding modules to the inline table, calls PyImport_AppendInittab()

must be made before Py_Initialize()

. The documentation PyImport_AppendInittab()

explicitly states:

Add one module to an existing inline module table .... This must be called before Py_Initialize()

.

Boost.Python uses a macro BOOST_PYTHON_MODULE

to define a Python module. Within a module of a module, the current scope is the module itself. Thus, when C ++ types are mapped through type wrappers, for example when C ++ classes are exposed to Python with boost::python::class_

, the resulting Python class will be inside the module defined BOOST_PYTHON_MODULE

.

On the other hand, user-defined types declared in Python are first-class objects. From a C ++ perspective, they can be thought of as a factory function. Hence, to use a specific Python class in C ++, you need to get a handle to the class object and then instantiate the class by calling the class object.




Here is a complete minimal example demonstrating the implementation of a Python 3 interpreter that:

  • Imports a module ( example

    ) that was built directly into the binary and provides a C ++ base class ( spam_wrap

    ) for Python ( example.Spam

    ) that has a default virtual function / dispatch.
  • Demonstrates the use of the Python public class ( example.Spam

    ).
  • Called from the public Python ( example.Spam

    ) class in 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 exit without error, resulting in the following output:

Hello from C++
Hello from Python

      

+4


source







All Articles