mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
pickling support (fixes #144)
This commit is contained in:
parent
505466ff0e
commit
1c329aab5a
@ -120,6 +120,7 @@ set(PYBIND11_EXAMPLES
|
|||||||
example/example12.cpp
|
example/example12.cpp
|
||||||
example/example13.cpp
|
example/example13.cpp
|
||||||
example/example14.cpp
|
example/example14.cpp
|
||||||
|
example/example15.cpp
|
||||||
example/issues.cpp
|
example/issues.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -841,7 +841,7 @@ objects (e.g. a NumPy matrix).
|
|||||||
The file :file:`example/example7.cpp` contains a complete example that
|
The file :file:`example/example7.cpp` contains a complete example that
|
||||||
demonstrates using the buffer protocol with pybind11 in more detail.
|
demonstrates using the buffer protocol with pybind11 in more detail.
|
||||||
|
|
||||||
.. [#f1] https://docs.python.org/3/c-api/buffer.html
|
.. [#f1] http://docs.python.org/3/c-api/buffer.html
|
||||||
|
|
||||||
NumPy support
|
NumPy support
|
||||||
=============
|
=============
|
||||||
@ -1184,3 +1184,74 @@ set of admissible operations.
|
|||||||
|
|
||||||
The file :file:`example/example14.cpp` contains a complete example that
|
The file :file:`example/example14.cpp` contains a complete example that
|
||||||
demonstrates how to create opaque types using pybind11 in more detail.
|
demonstrates how to create opaque types using pybind11 in more detail.
|
||||||
|
|
||||||
|
Pickling support
|
||||||
|
================
|
||||||
|
|
||||||
|
Python's ``pickle`` module provides a powerful facility to serialize and
|
||||||
|
de-serialize a Python object graph into a binary data stream. To pickle and
|
||||||
|
unpickle C++ classes using pybind11, two additional functions most be provided.
|
||||||
|
Suppose the class in question has the following signature:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class Pickleable {
|
||||||
|
public:
|
||||||
|
Pickleable(const std::string &value) : m_value(value) { }
|
||||||
|
const std::string &value() const { return m_value; }
|
||||||
|
|
||||||
|
void setExtra(int extra) { m_extra = extra; }
|
||||||
|
int extra() const { return m_extra; }
|
||||||
|
private:
|
||||||
|
std::string m_value;
|
||||||
|
int m_extra = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
The binding code including the requisite ``__setstate__`` and ``__getstate__`` methods [#f2]_
|
||||||
|
looks as follows:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
py::class_<Pickleable>(m, "Pickleable")
|
||||||
|
.def(py::init<std::string>())
|
||||||
|
.def("value", &Pickleable::value)
|
||||||
|
.def("extra", &Pickleable::extra)
|
||||||
|
.def("setExtra", &Pickleable::setExtra)
|
||||||
|
.def("__getstate__", [](const Pickleable &p) {
|
||||||
|
/* Return a tuple that fully encodes the state of the object */
|
||||||
|
return py::make_tuple(p.value(), p.extra());
|
||||||
|
})
|
||||||
|
.def("__setstate__", [](Pickleable &p, py::tuple t) {
|
||||||
|
if (t.size() != 2)
|
||||||
|
throw std::runtime_error("Invalid state!");
|
||||||
|
|
||||||
|
/* Invoke the constructor (need to use in-place version) */
|
||||||
|
new (&p) Pickleable(t[0].cast<std::string>());
|
||||||
|
|
||||||
|
/* Assign any additional state */
|
||||||
|
p.setExtra(t[1].cast<int>());
|
||||||
|
});
|
||||||
|
|
||||||
|
An instance can now be pickled as follows:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle as pickle # Use cPickle on Python 2.7
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
p = Pickleable("test_value")
|
||||||
|
p.setExtra(15)
|
||||||
|
data = cPickle.dumps(p, -1)
|
||||||
|
|
||||||
|
Note that only the cPickle module is supported on Python 2.7. It is also
|
||||||
|
important to request usage of the highest protocol version using the ``-1``
|
||||||
|
argument to ``dumps``.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
The file :file:`example/example15.cpp` contains a complete example that
|
||||||
|
demonstrates how to pickle and unpickle types using pybind11 in more detail.
|
||||||
|
|
||||||
|
.. [#f2] http://docs.python.org/3/library/pickle.html#pickling-class-instances
|
||||||
|
@ -5,8 +5,10 @@ Changelog
|
|||||||
|
|
||||||
1.5 (not yet released)
|
1.5 (not yet released)
|
||||||
----------------------
|
----------------------
|
||||||
|
* Pickling support
|
||||||
|
* Added a variadic ``make_tuple()`` function
|
||||||
* Address a rare issue that could confuse the current virtual function dispatcher
|
* Address a rare issue that could confuse the current virtual function dispatcher
|
||||||
* Documentation improvements: import issues, symbol visibility, limitations
|
* Documentation improvements: import issues, symbol visibility, pickling, limitations
|
||||||
|
|
||||||
1.4 (April 7, 2016)
|
1.4 (April 7, 2016)
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -23,6 +23,7 @@ void init_ex11(py::module &);
|
|||||||
void init_ex12(py::module &);
|
void init_ex12(py::module &);
|
||||||
void init_ex13(py::module &);
|
void init_ex13(py::module &);
|
||||||
void init_ex14(py::module &);
|
void init_ex14(py::module &);
|
||||||
|
void init_ex15(py::module &);
|
||||||
void init_issues(py::module &);
|
void init_issues(py::module &);
|
||||||
|
|
||||||
PYBIND11_PLUGIN(example) {
|
PYBIND11_PLUGIN(example) {
|
||||||
@ -42,6 +43,7 @@ PYBIND11_PLUGIN(example) {
|
|||||||
init_ex12(m);
|
init_ex12(m);
|
||||||
init_ex13(m);
|
init_ex13(m);
|
||||||
init_ex14(m);
|
init_ex14(m);
|
||||||
|
init_ex15(m);
|
||||||
init_issues(m);
|
init_issues(m);
|
||||||
|
|
||||||
return m.ptr();
|
return m.ptr();
|
||||||
|
51
example/example15.cpp
Normal file
51
example/example15.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
example/example15.cpp -- pickle support
|
||||||
|
|
||||||
|
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "example.h"
|
||||||
|
|
||||||
|
class Pickleable {
|
||||||
|
public:
|
||||||
|
Pickleable(const std::string &value) : m_value(value) { }
|
||||||
|
const std::string &value() const { return m_value; }
|
||||||
|
|
||||||
|
void setExtra1(int extra1) { m_extra1 = extra1; }
|
||||||
|
void setExtra2(int extra2) { m_extra2 = extra2; }
|
||||||
|
int extra1() const { return m_extra1; }
|
||||||
|
int extra2() const { return m_extra2; }
|
||||||
|
private:
|
||||||
|
std::string m_value;
|
||||||
|
int m_extra1 = 0;
|
||||||
|
int m_extra2 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_ex15(py::module &m) {
|
||||||
|
py::class_<Pickleable>(m, "Pickleable")
|
||||||
|
.def(py::init<std::string>())
|
||||||
|
.def("value", &Pickleable::value)
|
||||||
|
.def("extra1", &Pickleable::extra1)
|
||||||
|
.def("extra2", &Pickleable::extra2)
|
||||||
|
.def("setExtra1", &Pickleable::setExtra1)
|
||||||
|
.def("setExtra2", &Pickleable::setExtra2)
|
||||||
|
// For details on the methods below, refer to
|
||||||
|
// http://docs.python.org/3/library/pickle.html#pickling-class-instances
|
||||||
|
.def("__getstate__", [](const Pickleable &p) {
|
||||||
|
/* Return a tuple that fully encodes the state of the object */
|
||||||
|
return py::make_tuple(p.value(), p.extra1(), p.extra2());
|
||||||
|
})
|
||||||
|
.def("__setstate__", [](Pickleable &p, py::tuple t) {
|
||||||
|
if (t.size() != 3)
|
||||||
|
throw std::runtime_error("Invalid state!");
|
||||||
|
/* Invoke the constructor (need to use in-place version) */
|
||||||
|
new (&p) Pickleable(t[0].cast<std::string>());
|
||||||
|
|
||||||
|
/* Assign any additional state */
|
||||||
|
p.setExtra1(t[1].cast<int>());
|
||||||
|
p.setExtra2(t[2].cast<int>());
|
||||||
|
});
|
||||||
|
}
|
21
example/example15.py
Normal file
21
example/example15.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('.')
|
||||||
|
|
||||||
|
from example import Pickleable
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle as pickle # Use cPickle on Python 2.7
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
p = Pickleable("test_value")
|
||||||
|
p.setExtra1(15)
|
||||||
|
p.setExtra2(48)
|
||||||
|
|
||||||
|
data = pickle.dumps(p, -1) # -1 is important (use highest protocol version)
|
||||||
|
print("%s %i %i" % (p.value(), p.extra1(), p.extra2()))
|
||||||
|
|
||||||
|
p2 = pickle.loads(data)
|
||||||
|
print("%s %i %i" % (p2.value(), p2.extra1(), p2.extra2()))
|
2
example/example15.ref
Normal file
2
example/example15.ref
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
test_value 15 48
|
||||||
|
test_value 15 48
|
@ -142,6 +142,8 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> inline T cast() const { return operator object().cast<T>(); }
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
if (attr) {
|
if (attr) {
|
||||||
return (bool) PyObject_HasAttr(obj.ptr(), key.ptr());
|
return (bool) PyObject_HasAttr(obj.ptr(), key.ptr());
|
||||||
@ -161,18 +163,23 @@ private:
|
|||||||
struct list_accessor {
|
struct list_accessor {
|
||||||
public:
|
public:
|
||||||
list_accessor(handle list, size_t index) : list(list), index(index) { }
|
list_accessor(handle list, size_t index) : list(list), index(index) { }
|
||||||
|
|
||||||
void operator=(list_accessor o) { return operator=(object(o)); }
|
void operator=(list_accessor o) { return operator=(object(o)); }
|
||||||
|
|
||||||
void operator=(const handle &o) {
|
void operator=(const handle &o) {
|
||||||
// PyList_SetItem steals a reference to 'o'
|
// PyList_SetItem steals a reference to 'o'
|
||||||
if (PyList_SetItem(list.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
|
if (PyList_SetItem(list.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
|
||||||
pybind11_fail("Unable to assign value in Python list!");
|
pybind11_fail("Unable to assign value in Python list!");
|
||||||
}
|
}
|
||||||
|
|
||||||
operator object() const {
|
operator object() const {
|
||||||
PyObject *result = PyList_GetItem(list.ptr(), (ssize_t) index);
|
PyObject *result = PyList_GetItem(list.ptr(), (ssize_t) index);
|
||||||
if (!result)
|
if (!result)
|
||||||
pybind11_fail("Unable to retrieve value from Python list!");
|
pybind11_fail("Unable to retrieve value from Python list!");
|
||||||
return object(result, true);
|
return object(result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> inline T cast() const { return operator object().cast<T>(); }
|
||||||
private:
|
private:
|
||||||
handle list;
|
handle list;
|
||||||
size_t index;
|
size_t index;
|
||||||
@ -181,18 +188,23 @@ private:
|
|||||||
struct tuple_accessor {
|
struct tuple_accessor {
|
||||||
public:
|
public:
|
||||||
tuple_accessor(handle tuple, size_t index) : tuple(tuple), index(index) { }
|
tuple_accessor(handle tuple, size_t index) : tuple(tuple), index(index) { }
|
||||||
|
|
||||||
void operator=(tuple_accessor o) { return operator=(object(o)); }
|
void operator=(tuple_accessor o) { return operator=(object(o)); }
|
||||||
|
|
||||||
void operator=(const handle &o) {
|
void operator=(const handle &o) {
|
||||||
// PyTuple_SetItem steals a referenceto 'o'
|
// PyTuple_SetItem steals a referenceto 'o'
|
||||||
if (PyTuple_SetItem(tuple.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
|
if (PyTuple_SetItem(tuple.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
|
||||||
pybind11_fail("Unable to assign value in Python tuple!");
|
pybind11_fail("Unable to assign value in Python tuple!");
|
||||||
}
|
}
|
||||||
|
|
||||||
operator object() const {
|
operator object() const {
|
||||||
PyObject *result = PyTuple_GetItem(tuple.ptr(), (ssize_t) index);
|
PyObject *result = PyTuple_GetItem(tuple.ptr(), (ssize_t) index);
|
||||||
if (!result)
|
if (!result)
|
||||||
pybind11_fail("Unable to retrieve value from Python tuple!");
|
pybind11_fail("Unable to retrieve value from Python tuple!");
|
||||||
return object(result, true);
|
return object(result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> inline T cast() const { return operator object().cast<T>(); }
|
||||||
private:
|
private:
|
||||||
handle tuple;
|
handle tuple;
|
||||||
size_t index;
|
size_t index;
|
||||||
|
Loading…
Reference in New Issue
Block a user