You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
2.9 KiB

2 months ago
  1. /*
  2. tests/test_pickling.cpp -- pickle support
  3. Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
  4. All rights reserved. Use of this source code is governed by a
  5. BSD-style license that can be found in the LICENSE file.
  6. */
  7. #include "pybind11_tests.h"
  8. class Pickleable {
  9. public:
  10. Pickleable(const std::string &value) : m_value(value) { }
  11. const std::string &value() const { return m_value; }
  12. void setExtra1(int extra1) { m_extra1 = extra1; }
  13. void setExtra2(int extra2) { m_extra2 = extra2; }
  14. int extra1() const { return m_extra1; }
  15. int extra2() const { return m_extra2; }
  16. private:
  17. std::string m_value;
  18. int m_extra1 = 0;
  19. int m_extra2 = 0;
  20. };
  21. class PickleableWithDict {
  22. public:
  23. PickleableWithDict(const std::string &value) : value(value) { }
  24. std::string value;
  25. int extra;
  26. };
  27. test_initializer pickling([](py::module &m) {
  28. py::class_<Pickleable>(m, "Pickleable")
  29. .def(py::init<std::string>())
  30. .def("value", &Pickleable::value)
  31. .def("extra1", &Pickleable::extra1)
  32. .def("extra2", &Pickleable::extra2)
  33. .def("setExtra1", &Pickleable::setExtra1)
  34. .def("setExtra2", &Pickleable::setExtra2)
  35. // For details on the methods below, refer to
  36. // http://docs.python.org/3/library/pickle.html#pickling-class-instances
  37. .def("__getstate__", [](const Pickleable &p) {
  38. /* Return a tuple that fully encodes the state of the object */
  39. return py::make_tuple(p.value(), p.extra1(), p.extra2());
  40. })
  41. .def("__setstate__", [](Pickleable &p, py::tuple t) {
  42. if (t.size() != 3)
  43. throw std::runtime_error("Invalid state!");
  44. /* Invoke the constructor (need to use in-place version) */
  45. new (&p) Pickleable(t[0].cast<std::string>());
  46. /* Assign any additional state */
  47. p.setExtra1(t[1].cast<int>());
  48. p.setExtra2(t[2].cast<int>());
  49. });
  50. #if !defined(PYPY_VERSION)
  51. py::class_<PickleableWithDict>(m, "PickleableWithDict", py::dynamic_attr())
  52. .def(py::init<std::string>())
  53. .def_readwrite("value", &PickleableWithDict::value)
  54. .def_readwrite("extra", &PickleableWithDict::extra)
  55. .def("__getstate__", [](py::object self) {
  56. /* Also include __dict__ in state */
  57. return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__"));
  58. })
  59. .def("__setstate__", [](py::object self, py::tuple t) {
  60. if (t.size() != 3)
  61. throw std::runtime_error("Invalid state!");
  62. /* Cast and construct */
  63. auto& p = self.cast<PickleableWithDict&>();
  64. new (&p) PickleableWithDict(t[0].cast<std::string>());
  65. /* Assign C++ state */
  66. p.extra = t[1].cast<int>();
  67. /* Assign Python state */
  68. self.attr("__dict__") = t[2];
  69. });
  70. #endif
  71. });