Spaces:
Runtime error
Runtime error
/* | |
tests/test_pickling.cpp -- pickle support | |
Copyright (c) 2016 Wenzel Jakob <[email protected]> | |
All rights reserved. Use of this source code is governed by a | |
BSD-style license that can be found in the LICENSE file. | |
*/ | |
TEST_SUBMODULE(pickling, m) { | |
// test_roundtrip | |
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; | |
}; | |
class PickleableNew : public Pickleable { | |
public: | |
using Pickleable::Pickleable; | |
}; | |
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>()); | |
}); | |
py::class_<PickleableNew, Pickleable>(m, "PickleableNew") | |
.def(py::init<std::string>()) | |
.def(py::pickle( | |
[](const PickleableNew &p) { | |
return py::make_tuple(p.value(), p.extra1(), p.extra2()); | |
}, | |
[](py::tuple t) { | |
if (t.size() != 3) | |
throw std::runtime_error("Invalid state!"); | |
auto p = PickleableNew(t[0].cast<std::string>()); | |
p.setExtra1(t[1].cast<int>()); | |
p.setExtra2(t[2].cast<int>()); | |
return p; | |
} | |
)); | |
// test_roundtrip_with_dict | |
class PickleableWithDict { | |
public: | |
PickleableWithDict(const std::string &value) : value(value) { } | |
std::string value; | |
int extra; | |
}; | |
class PickleableWithDictNew : public PickleableWithDict { | |
public: | |
using PickleableWithDict::PickleableWithDict; | |
}; | |
py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr()) | |
.def(py::init<std::string>()) | |
.def_readwrite("value", &PickleableWithDict::value) | |
.def_readwrite("extra", &PickleableWithDict::extra) | |
.def("__getstate__", [](py::object self) { | |
/* Also include __dict__ in state */ | |
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); | |
}) | |
.def("__setstate__", [](py::object self, py::tuple t) { | |
if (t.size() != 3) | |
throw std::runtime_error("Invalid state!"); | |
/* Cast and construct */ | |
auto& p = self.cast<PickleableWithDict&>(); | |
new (&p) PickleableWithDict(t[0].cast<std::string>()); | |
/* Assign C++ state */ | |
p.extra = t[1].cast<int>(); | |
/* Assign Python state */ | |
self.attr("__dict__") = t[2]; | |
}); | |
py::class_<PickleableWithDictNew, PickleableWithDict>(m, "PickleableWithDictNew") | |
.def(py::init<std::string>()) | |
.def(py::pickle( | |
[](py::object self) { | |
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); | |
}, | |
[](const py::tuple &t) { | |
if (t.size() != 3) | |
throw std::runtime_error("Invalid state!"); | |
auto cpp_state = PickleableWithDictNew(t[0].cast<std::string>()); | |
cpp_state.extra = t[1].cast<int>(); | |
auto py_state = t[2].cast<py::dict>(); | |
return std::make_pair(cpp_state, py_state); | |
} | |
)); | |
} | |