Make std::map pickable #5266
-
Hello,
but I am having trouble with the
Is it possible to make this std::map pickable? If so, Can anyone point me in the right direction? Any help would be much appreciated. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi @alvaro-diaz-valenzuela you need to manually cast to desired pair in a loop. Automatic iteration over py::tuple is simply catching py::handle instances -- thus the translation to std::pair is simply unknown, you probably would need Here is what you need to do (I have simplified the class QCDate; // forward declarations
PYBIND11_MAKE_OPAQUE(std::map<QCDate, double>);
namespace py = pybind11;
class QCDate {
public:
QCDate() {}
QCDate(int v) : value{v} {}
bool operator<(const QCDate& other) const
{
return value < other.value;
}
int value;
};
PYBIND11_MODULE(mymodule, m)
{
m.doc() = "This is the module test";
py::class_<QCDate>(m, "QCDate")
.def(py::init<>())
.def(py::init<int>())
.def(py::pickle(
[](const QCDate &self) { // __getstate__
return py::make_tuple(self.value);
},
[](py::tuple t) { // __setstate__
if (t.size() != 1)
throw std::runtime_error("Invalid state!");
QCDate obj(t[0].cast<int>());
return obj;
}
))
.def("__repr__", [](QCDate &self){
return std::string("QCDate:") + std::to_string(self.value);
});
py::bind_map<std::map<QCDate, double>>(m, "time_series")
.def(py::pickle(
[](std::map<QCDate, double> &self) { // __getstate__
std::vector<py::tuple> items;
for (auto& element : self) {
items.emplace_back(py::cast(element));
}
return py::make_tuple(items);
},
[](py::tuple t) { // __setstate__
if (t.size() != 1)
throw std::runtime_error("Invalid state!");
std::map<QCDate, double> result;
for(auto tuple_item : t[0]) {
auto p = tuple_item.cast<std::pair<QCDate, double>>();
result[p.first] = p.second;
}
return result;
}
));
} Here is code snippet for Python: import mymodule
import pickle
ts = mymodule.time_series()
ts[mymodule.QCDate(21)] = 4.2
ts[mymodule.QCDate(37)] = 2.5
ts[mymodule.QCDate(42)] = 9.6
p = pickle.dumps(ts)
loaded = pickle.loads(p)
print(dict(loaded)) # that will print: {QCDate:21: 4.2, QCDate:37: 2.5, QCDate:42: 9.6} Hope that helps, cheers! PS I have edited the answer as I missed the custom map in testing:) |
Beta Was this translation helpful? Give feedback.
Hi @alvaro-diaz-valenzuela you need to manually cast to desired pair in a loop. Automatic iteration over py::tuple is simply catching py::handle instances -- thus the translation to std::pair is simply unknown, you probably would need
type_caster
for it. However, there is a simpler way, written below.Here is what you need to do (I have simplified the
QCData
):