Reputation: 1106
I wrote a class with a member that is a nested unordered_map
with a custom type as key and another unordered_map
as value.
I would like to serialize/deserialize the inner map using the boost::serialization
library.
However, even if the code is mostly done, the compiler returns this error code (I cut it down and simplified it a bit):
class std::unordered_map<
const MyClass<...>::key_type,
unsigned int,
MyClass<...>::key_hash,
std::equal_to<const MyClass<...>::key_type>,
std::allocator<std::pair<const MyClass<...>::key_type, unsigned int> > >’ has no member named ‘serialize’
From what I understand, it tells me that there is no member serialize
for this map. However, I included the boost
headers that should fix this (but it is not).
I suspect that the fact I declared the key as being const
in the map declaration messes with the library, but I'm unable (aka not knowledgeable enough) to make sure of it: when I remove the const
qualifier I end up with a bunch of other compilation errors.
The code for my class is the following:
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <unordered_map>
template<typename A, typename B, typename C>
class MyClass
{
public:
using coord_type = another_serializable_and_tested_class;
struct key_type
{
coord_type from;
coord_type to;
key_type(coord_type const& origin, coord_type const& destination):
from(origin),
to(destination)
{}
bool operator==(key_type const& other) const
{
return (
other.from == this->from &&
other.to == this->to
);
}
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & from;
ar & to;
}
}; // end struct key_type
struct key_hash : public std::unary_function<key_type, std::size_t>
{
std::size_t operator()(const key_type& k) const
{
size_t res = 17;
res = res * 31 + std::hash<coord_type>()( k.from );
res = res * 31 + std::hash<coord_type>()( k.to );
return res;
}
}; // end struct key_hash
using forward_flow_type = std::unordered_map<time_type, std::unordered_map<const key_type, value_type, key_hash>>;
// other members and functions, including the serialization/deserialization operations:
void deserialize_layer(int t) const
{
const std::string forward_filename = get_forward_flow_archive_name(t);
// create and open an archive for input
std::ifstream forward_ifs(forward_filename, std::ios::binary);
boost::archive::binary_iarchive forward_ia(forward_ifs);
// read class state from archive
std::unordered_map<const key_type, value_type, key_hash> forward_layer;
forward_ia >> forward_layer;
// archive and stream closed when destructors are called
m_forward_flow.emplace(t,forward_layer);
}
};
Any idea what is going wrong?
Upvotes: 2
Views: 371
Reputation: 392921
Do yourself a favor and unkludge your types:
using Layer = std::unordered_map<key_type, value_type, key_hash>;
using forward_flow_type = std::unordered_map<time_type, Layer>;
The const
was misguided, that's up to the container implementation.
Next,
void deserialize_layer(int t) const
is marked const
, yet you ...
m_forward_flow.emplace(t, forward_layer);
That's not going to compile. So, drop the const.
The remaining issue is that your key type is not default constructible. The easiest way is to just add that:
key_type(coord_type const& origin = {},
coord_type const& destination = {})
: from(origin)
, to(destination)
{
}
The complicated way is to implement serialization using save_construct_data
and load_construct_data
.
This adds just enough machinery to do a roundtrip of a single frame filled with demo data:
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/functional/hash.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/utility.hpp>
#include <fstream>
#include <iostream>
#include <unordered_map>
auto get_forward_flow_archive_name(int t)
{
return "forward_flow_" + std::to_string(t) + ".dat";
}
struct another_serializable_and_tested_class {
double x, y;
void serialize(auto& ar, unsigned) { ar & x & y; }
bool operator==(another_serializable_and_tested_class const&) const = default;
friend size_t hash_value(another_serializable_and_tested_class const& o) {
auto h = boost::hash_value(o.x);
boost::hash_combine(h, boost::hash_value(o.y));
return h;
}
friend std::ostream& operator<<(std::ostream& os, another_serializable_and_tested_class const& p)
{
return os << "(" << p.x << ", " << p.y << ")";
}
};
template <typename A, typename B, typename C> class MyClass {
public:
using coord_type = another_serializable_and_tested_class;
struct key_type {
coord_type from;
coord_type to;
key_type(coord_type const& origin = {},
coord_type const& destination = {})
: from(origin)
, to(destination)
{
}
bool operator==(key_type const& other) const
{
return (other.from == this->from && other.to == this->to);
}
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar& from;
ar& to;
}
}; // end struct key_type
struct key_hash {
std::size_t operator()(const key_type& k) const
{
size_t res = 17;
boost::hash_combine(res, k.from);
boost::hash_combine(res, k.to);
return res;
}
}; // end struct key_hash
using time_type = int; // stub
using value_type = long; // stub
using Layer = std::unordered_map<key_type, value_type, key_hash>;
using forward_flow_type = std::unordered_map<time_type, Layer>;
forward_flow_type m_forward_flow;
// other members and functions, including the serialization/deserialization
// operations:
void serialize_layer(int t) const
{
const std::string forward_filename = get_forward_flow_archive_name(t);
std::ofstream forward_ofs(forward_filename, std::ios::binary);
boost::archive::binary_oarchive forward_oa(forward_ofs);
forward_oa << m_forward_flow.at(t);
}
// other members and functions, including the serialization/deserialization
// operations:
void deserialize_layer(int t)
{
const std::string forward_filename = get_forward_flow_archive_name(t);
// create and open an archive for input
std::ifstream forward_ifs(forward_filename, std::ios::binary);
boost::archive::binary_iarchive forward_ia(forward_ifs);
// read class state from archive
Layer forward_layer;
forward_ia >> forward_layer;
// archive and stream closed when destructors are called
m_forward_flow.emplace(t, forward_layer);
}
};
int main() {
using Object = MyClass<void, void, void>;
{
Object obj;
auto& layer42 = obj.m_forward_flow.emplace(42, Object::Layer{}).first->second;
for (auto& [k, v] : {
std::tuple(
Object::key_type{
{1, 2}, // from
{3, 4} // to
},
5),
std::tuple(Object::key_type{{6, 7}, {8, 9}}, 10),
std::tuple(Object::key_type{{11, 12}, {13, 14}}, 15),
std::tuple(Object::key_type{{16, 17}, {18, 19}}, 20),
std::tuple(Object::key_type{{21, 22}, {23, 24}}, 25),
std::tuple(Object::key_type{{26, 27}, {28, 29}}, 30),
})
{
layer42.emplace(k, v);
}
obj.serialize_layer(42);
}
{
Object obj;
obj.deserialize_layer(42);
for (auto& [k, v] : obj.m_forward_flow.at(42)) {
auto& [from,to] = k;
std::cout << "From " << from << " to " << to << " value " << v << "\n";
}
}
}
Prints
From (1, 2) to (3, 4) value 5
From (6, 7) to (8, 9) value 10
From (11, 12) to (13, 14) value 15
From (16, 17) to (18, 19) value 20
From (21, 22) to (23, 24) value 25
From (26, 27) to (28, 29) value 30
as expected. The file (on my machine) contains:
00000000: 1600 0000 0000 0000 7365 7269 616c 697a ........serializ
00000010: 6174 696f 6e3a 3a61 7263 6869 7665 0f00 ation::archive..
00000020: 0408 0408 0100 0000 0000 0000 0006 0000 ................
00000030: 0000 0000 000d 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 3a40 0000 0000 0000 3b40 ......:@......;@
00000060: 0000 0000 0000 3c40 0000 0000 0000 3d40 ......<@......=@
00000070: 1e00 0000 0000 0000 0000 0000 0000 3540 ..............5@
00000080: 0000 0000 0000 3640 0000 0000 0000 3740 [email protected]@
00000090: 0000 0000 0000 3840 1900 0000 0000 0000 ......8@........
000000a0: 0000 0000 0000 3040 0000 0000 0000 3140 [email protected]@
000000b0: 0000 0000 0000 3240 0000 0000 0000 3340 [email protected]@
000000c0: 1400 0000 0000 0000 0000 0000 0000 2640 ..............&@
000000d0: 0000 0000 0000 2840 0000 0000 0000 2a40 ......(@......*@
000000e0: 0000 0000 0000 2c40 0f00 0000 0000 0000 ......,@........
000000f0: 0000 0000 0000 1840 0000 0000 0000 1c40 .......@.......@
00000100: 0000 0000 0000 2040 0000 0000 0000 2240 ...... @......"@
00000110: 0a00 0000 0000 0000 0000 0000 0000 f03f ...............?
00000120: 0000 0000 0000 0040 0000 0000 0000 0840 .......@.......@
00000130: 0000 0000 0000 1040 0500 0000 0000 0000 .......@........
Upvotes: 1