I'm using the cereal library to serialize my classes into files, but am running into trouble with std::map
- specifically, maps that use std::filesystem::path
.
Say I have an Object
class that contains only a map<fs::path, fs::path>
, as well as the required serialize function for cereal:
struct Object
{
map<fs::path, fs::path> _map;
template<class Archive>
void serialize(Archive & archive)
{
archive(_map);
}
friend ostream& operator<<(ostream& outs, const Object c)
{
for (auto const &pair: c._map)
std::cout << "{" << pair.first << ": " << pair.second << "}\n";
return outs;
}
};
In my main function, I have:
int main()
{
// create Object
cout << "Creating object..." << endl;
Object o;
fs::path a = "bye.txt";
fs::path b = "hello.txt";
o._map[a] = b;
o._map[b] = a;
cout << "Object created: " << endl;
cout << o;
// serialize
cout << "Serializing object...." << endl;
stringstream ss;
cereal::BinaryOutputArchive oarchive(ss);
oarchive(o);
cout << "Object serialized." << endl;
// write to file
cout << "Writing serialized object to file...." << endl;
ofstream file("serialized_object");
file << ss.str();
file.close();
cout << "Object written to file." << endl;
// read from file
cout << "Reading from file..." << endl;
stringstream ss2;
fs::path ins = "serialized_object";
ifstream file_stream(ins, ios::binary);
ss2 << file_stream.rdbuf();
cereal::BinaryInputArchive iarchive(ss2);
Object out;
iarchive(out);
cout << "Object read from file." << endl;
cout << out;
}
In my output, I see the error when it reads from the serialized file:
Creating object...
Object created:
{"bye.txt": "hello.txt"}
{"hello.txt": "bye.txt"}
Serializing object....
Object serialized.
Writing serialized object to file....
Object written to file.
Reading from file...
terminate called after throwing an instance of 'cereal::Exception'
what(): Failed to read 2573 bytes from input stream! Read 28
My includes are:
#include <iostream>
#include <filesystem>
#include <fstream>
#include <string.h>
#include <map>
#include "cereal/types/map.hpp"
#include "cereal/archives/binary.hpp"
#include "cereal/types/string.hpp"
And I have included the following code at the beginning in order to be able to serialize fs::path
:
namespace std
{
namespace filesystem
{
template<class Archive>
void CEREAL_LOAD_MINIMAL_FUNCTION_NAME(const Archive&, path& out, const string& in)
{
out = in;
}
template<class Archive>
string CEREAL_SAVE_MINIMAL_FUNCTION_NAME(const Archive& ar, const path& p)
{
return p.string();
}
}
}
I'm sure I'm missing something obvious, as this is my first time using cereal. Does anyone have any insight as to why I'm running into this issue?
Based on the cereal documentation, you must always use the
ios::binary
flag when utilizing the BinaryArchive. Here is the specific section from this page in their documentation:The other issue is based on how Cereal guarantees that the serialization has completed. Per this link below, the output archive object should go out of scope (invoking the destructor) prior to any attempt to read the archive. They utilize the RAII approach, just like
ifstream
andofstream
as noted here.You can greatly simplify your code by allowing the Cereal library perform the writes and reads internally. Here is a modified version of your main function:
I hope this helps. Please test this all out prior to utilization.