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