...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
It is often necessary to save and restore the contents of an object to a file. One approach to this problem is to write a pair of functions that read and write data from a file in a special format. A powerful alternative approach is to use Python's pickle module. Exploiting Python's ability for introspection, the pickle module recursively converts nearly arbitrary Python objects into a stream of bytes that can be written to a file.
The Boost Python Library supports the pickle module through the interface as described in detail in the Python Library Reference for pickle. This interface involves the special methods __getinitargs__, __getstate__ and __setstate__ as described in the following. Note that Boost.Python is also fully compatible with Python's cPickle module.
If __getinitargs__ is not defined, pickle.load will call the constructor (__init__) without arguments; i.e., the object must be default-constructible.
struct world_pickle_suite : boost::python::pickle_suite { static boost::python::tuple getinitargs(world const& w) { return boost::python::make_tuple(w.get_country()); } };
class_<world>("world", args<const std::string&>()) // ... .def_pickle(world_pickle_suite()) // ...
struct world_pickle_suite : boost::python::pickle_suite { static boost::python::tuple getinitargs(const world& w) { // ... } static boost::python::tuple getstate(const world& w) { // ... } static void setstate(world& w, boost::python::tuple state) { // ... } };
class_<world>("world", args<const std::string&>()) // ... .def_pickle(world_pickle_suite()) // ...
For simplicity, the __dict__ is not included in the result of __getstate__. This is not generally recommended, but a valid approach if it is anticipated that the object's __dict__ will always be empty. Note that the safety guard described below will catch the cases where this assumption is violated.
__getstate__ is defined and the instance's __dict__ is not empty.
The author of a Boost.Python extension class might provide a __getstate__ method without considering the possibilities that:
To alert the user to this highly unobvious problem, a safety guard is provided. If __getstate__ is defined and the instance's __dict__ is not empty, Boost.Python tests if the class has an attribute __getstate_manages_dict__. An exception is raised if this attribute is not defined:
RuntimeError: Incomplete pickle support (__getstate_manages_dict__ not set)To resolve this problem, it should first be established that the __getstate__ and __setstate__ methods manage the instances's __dict__ correctly. Note that this can be done either at the C++ or the Python level. Finally, the safety guard should intentionally be overridden. E.g. in C++ (from pickle3.cpp):
struct world_pickle_suite : boost::python::pickle_suite { // ... static bool getstate_manages_dict() { return true; } };Alternatively in Python:
import your_bpl_module class your_class(your_bpl_module.your_class): __getstate_manages_dict__ = 1 def __getstate__(self): # your code here def __setstate__(self, state): # your code here
class_<world>("world", args<const std::string&>()) // ... .enable_pickling() // ...This enables the standard Python pickle interface as described in the Python documentation. By "injecting" a __getinitargs__ method into the definition of the wrapped class we make all instances pickleable:
# import the wrapped world class from pickle4_ext import world # definition of __getinitargs__ def world_getinitargs(self): return (self.get_country(),) # now inject __getinitargs__ (Python is a dynamic language!) world.__getinitargs__ = world_getinitargsSee also the tutorial section on injecting additional methods from Python.
Updated: Feb 2004.