Bryan Fok
Bryan Fok

Reputation: 3487

boost python enable_pickling expectation

Hi I am using python to initiate a cpp class which use boost python lib to convert into python usable. at the same time, i have a requirement to pickle the python classes that use the python enabled cpp class.

So what i did is to add enable_picking() to an example class definition like this:

class_<pform::base::Price>("Price", init<double>())
      .def(self == self)
      .def(self_ns::str(self_ns::self))  // __str__
      .def("get_value", &pform::base::Price::get_value)

it make the class pickleable. However i get this error when unpickle it.

Boost.Python.ArgumentError: Python argument types in
    Price.__init__(Price)
did not match C++ signature:
    __init__(_object*, double)

So what is missing here?

Upvotes: 2

Views: 1438

Answers (1)

csiz
csiz

Reputation: 5190

A bit late, but I found the relevant boost documentation for this:

http://www.boost.org/doc/libs/1_64_0/libs/python/doc/html/reference/topics/pickle_support.html

The Pickle Interface

At the user level, the Boost.Python pickle interface involves three special methods:

  • __getinitargs__ When an instance of a Boost.Python extension class is pickled, the pickler tests if the instance has a __getinitargs__ method. This method must return a Python tuple (it is most convenient to use a boost::python::tuple). When the instance is restored by the unpickler, the contents of this tuple are used as the arguments for the class constructor. If __getinitargs__ is not defined, pickle.load will call the constructor (__init__) without arguments; i.e., the object must be default-constructible.

  • __getstate__ When an instance of a Boost.Python extension class is pickled, the pickler tests if the instance has a __getstate__ method. This method should return a Python object representing the state of the instance.

  • __setstate__ When an instance of a Boost.Python extension class is restored by the unpickler (pickle.load), it is first constructed using the result of __getinitargs__ as arguments (see above). Subsequently the unpickler tests if the new instance has a __setstate__ method. If so, this method is called with the result of __getstate__ (a Python object) as the argument.

The three special methods described above may be .def()'ed individually by the user. However, Boost.Python provides an easy to use high-level interface via the boost::python::pickle_suite class that also enforces consistency: __getstate__ and __setstate__ must be defined as pairs. Use of this interface is demonstrated by the following examples.

In your particular example the class is not default constructible as it needs a double argument (which I assume is the "value"). To wrap it for Python you would also need to define:

.def("__getinitargs__", +[](pform::base::Price const& self){
  return boost::python::make_tuple(self.get_value());
})

Now Boost Python will initialize your class using "value"; instead of calling the default constructor (pform::base::Price()).

Upvotes: 2

Related Questions