mirror of https://github.com/pybind/pybind11
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.
195 lines
6.5 KiB
195 lines
6.5 KiB
// clang-format off |
|
/* |
|
tests/test_pickling.cpp -- pickle support |
|
|
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> |
|
Copyright (c) 2021 The Pybind Development Team. |
|
|
|
All rights reserved. Use of this source code is governed by a |
|
BSD-style license that can be found in the LICENSE file. |
|
*/ |
|
|
|
#include "pybind11_tests.h" |
|
|
|
// clang-format on |
|
|
|
#include <memory> |
|
#include <stdexcept> |
|
#include <utility> |
|
|
|
namespace exercise_trampoline { |
|
|
|
struct SimpleBase { |
|
int num = 0; |
|
virtual ~SimpleBase() = default; |
|
|
|
// For compatibility with old clang versions: |
|
SimpleBase() = default; |
|
SimpleBase(const SimpleBase &) = default; |
|
}; |
|
|
|
struct SimpleBaseTrampoline : SimpleBase {}; |
|
|
|
struct SimpleCppDerived : SimpleBase {}; |
|
|
|
void wrap(py::module m) { |
|
py::class_<SimpleBase, SimpleBaseTrampoline>(m, "SimpleBase") |
|
.def(py::init<>()) |
|
.def_readwrite("num", &SimpleBase::num) |
|
.def(py::pickle( |
|
[](const py::object &self) { |
|
py::dict d; |
|
if (py::hasattr(self, "__dict__")) { |
|
d = self.attr("__dict__"); |
|
} |
|
return py::make_tuple(self.attr("num"), d); |
|
}, |
|
[](const py::tuple &t) { |
|
if (t.size() != 2) { |
|
throw std::runtime_error("Invalid state!"); |
|
} |
|
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline); |
|
cpp_state->num = t[0].cast<int>(); |
|
auto py_state = t[1].cast<py::dict>(); |
|
return std::make_pair(std::move(cpp_state), py_state); |
|
})); |
|
|
|
m.def("make_SimpleCppDerivedAsBase", |
|
[]() { return std::unique_ptr<SimpleBase>(new SimpleCppDerived); }); |
|
m.def("check_dynamic_cast_SimpleCppDerived", [](const SimpleBase *base_ptr) { |
|
return dynamic_cast<const SimpleCppDerived *>(base_ptr) != nullptr; |
|
}); |
|
} |
|
|
|
} // namespace exercise_trampoline |
|
|
|
// clang-format off |
|
|
|
TEST_SUBMODULE(pickling, m) { |
|
// test_roundtrip |
|
class Pickleable { |
|
public: |
|
explicit 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> pyPickleable(m, "Pickleable"); |
|
pyPickleable |
|
.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()); |
|
}); |
|
ignoreOldStyleInitWarnings([&pyPickleable]() { |
|
pyPickleable.def("__setstate__", [](Pickleable &p, const 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()); |
|
}, |
|
[](const 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; |
|
})); |
|
|
|
#if !defined(PYPY_VERSION) |
|
// test_roundtrip_with_dict |
|
class PickleableWithDict { |
|
public: |
|
explicit PickleableWithDict(const std::string &value) : value(value) { } |
|
|
|
std::string value; |
|
int extra; |
|
}; |
|
|
|
class PickleableWithDictNew : public PickleableWithDict { |
|
public: |
|
using PickleableWithDict::PickleableWithDict; |
|
}; |
|
|
|
py::class_<PickleableWithDict> pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr()); |
|
pyPickleableWithDict.def(py::init<std::string>()) |
|
.def_readwrite("value", &PickleableWithDict::value) |
|
.def_readwrite("extra", &PickleableWithDict::extra) |
|
.def("__getstate__", [](const py::object &self) { |
|
/* Also include __dict__ in state */ |
|
return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); |
|
}); |
|
ignoreOldStyleInitWarnings([&pyPickleableWithDict]() { |
|
pyPickleableWithDict.def("__setstate__", [](const py::object &self, const 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( |
|
[](const 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); |
|
})); |
|
#endif |
|
|
|
exercise_trampoline::wrap(m); |
|
}
|
|
|