Setting up embedded Python for Scripting a C++ Game
Question: I'm having trouble achieving this. What I'm stuck with is trying to expose
Modules written in C++ to an embedded python interpreter.

I'm using boost::python, but I'm not sure what I'm supposed to do for this, as
the documentation seems to be lacking, to say the least.

What I want is to expose some C++ code with BOOST_PYTHON_MODULE, and then
access that from _the same application_. However I can't get it to import.
What I've got, which seem the closest (just relevant part):

    
    
    #include <python/interpreter.hpp>
    
    bp::object blag() {
        return bp::str("Thingy");
    }
    
    BOOST_PYTHON_MODULE(modthingy) {
        bp::def("blag", &blag);
    }
    
    
    Interpreter::Interpreter() {
        Py_UnbufferedStdioFlag = 1;
        Py_Initialize();
        try {
            init_module_modthingy();
        } catch (bp::error_already_set) {
            PyErr_Print();
        }
    
        main_module = bp::import("__main__");
        main_namespace = main_module.attr("__dict__");
    }
    

But that prints the Error `AttributeError: 'NoneType' object has no attribute
'__dict__'` And I can't import the module later.

How should this be structured?

EDIT: Ok, so the closest I got was one of the methods in the accepted answer:

    
    
    PyImport_AppendInittab("modthingy", &PyInit_modthingy);
    Py_Initialize();
    

However, this doesn't seem particularly useful in my case, as I'd like to be
able to add/import modules after the Initialize function. I'm going to look
into a few things, namely:

  * See if I can get the suggested approach for python 2 working in python 3
  * See if I can nicely structure my game to require naming all of the modules before Py_Initialize

I'll update this post with my findings.


Answer: Boost.Python uses the
[`BOOST_PYTHON_MODULE`](http://www.boost.org/doc/libs/1_57_0/libs/python/doc/v2/module.html#BOOST_PYTHON_MODULE-
spec) macro to define a Python module initializer. The resulting function is
not the module importer. This difference is similar to that of creating a
`modthingy.py` module and calling `import modthingy`.

When importing a module, Python will first check if the module is a built-in
module. If the module is not there, then Python will then search the [module
search path](https://docs.python.org/3/tutorial/modules.html#the-module-
search-path) trying to find a python file or library based on the module name.
If a library is found, then Python expects the library to provide a function
that will initialize the module. Once found, the import will create an empty
module in the modules table, then initialize it. For statically linked
modules, such as `modthingy`, the module search path will not be helpful, as
there is no library for it to find.

For embedding, the [module table and initialization
function](https://docs.python.org/3/extending/extending.html#the-module-s-
method-table-and-initialization-function) documentation states that for static
modules, the module initializer function will not be automatically called
unless there is an entry in the initialization table. For Python 2 and Python
3, one can accomplish this by calling
[`PyImport_AppendInittab()`](https://docs.python.org/3/c-api/import.html#c.PyImport_AppendInittab)
before
[`Py_Initialize()`](https://docs.python.org/3/c-api/init.html#c.Py_Initialize):

    
    
    BOOST_PYTHON_MODULE(modthingy)
    {
      // ...
    }
    
    PyImport_AppendInittab("modthingy", &initmodthingy);
    Py_Initialize();
    // ...
    boost::python::object modthingy = boost::python::import("modthingy");
    

Alternatively, for Python 2, once the interpreter has been initialized, one
can create an empty module that is added to the modules dictionary via
[`PyImport_AddModule()`](https://docs.python.org/2/c-api/import.html#c.PyImport_AddModule),
then explicitly initialize the module.

    
    
    BOOST_PYTHON_MODULE(modthingy)
    {
      // ...
    }
    
    Py_Initialize();
    PyImport_AddModule("modythingy");
    initmodthingy();
    boost::python::object modthingy = boost::python::import("modthingy");
    

This approach is demonstrated in the official Python embedded demo,
[embed/demo.c](https://github.com/python-
git/python/blob/master/Demo/embed/demo.c). The module initializer created from
`BOOST_PYTHON_MODULE` does not call `PyImport_AddModule()`, thus it must be
explicitly called.

Also note that the Python's C API for embedding changed naming conventions for
module initialization functions between Python 2 and 3, so for
`BOOST_PYTHON_MODULE(modthingy)`, one may need to use `&initmodthingy` for
Python 2 and `&PyInit_modthingy` for Python 3.

* * *

Here is a minimal complete example demonstrating importing a module statically
linked with the embedded interpreter:

    
    
    #include <iostream>
    #include <string>
    
    #include <boost/python.hpp>
    
    std::string spam() { return "Spam spam spam"; }
    
    BOOST_PYTHON_MODULE(example)
    {
      namespace python = boost::python;
      python::def("spam", &spam);
    }
    
    int main()
    {
      // Add example to built-in.
      PyImport_AppendInittab("example", &initexample);
    
      // Start the interpreter.
      Py_Initialize();
    
      namespace python = boost::python;
      try
      {
        // >>> import example
        python::object example = python::import("example");
        // >>> x = example.spam()
        python::object x = example.attr("spam")();
        // >>> print x
        std::cout << "x = " << python::extract<std::string>(x)() << std::endl;
      }
      catch (const python::error_already_set&)
      {
        PyErr_Print();
      }
    }
    

Output:

    
    
    x = Spam spam spam
    

