I haven't been able to find any docs on how to serialize a unique_ptr to an array. Any help would be great.
struct Counter{
int index;
unique_ptr<char []> name;
template<class Archive>
void serialize(Archive & archive){
archive(index, name ); // serialize things by passing them to the archive
}
};
How it is assigned.
auto buffer = std::unique_ptr<char[]>(new char[BUFFER_SIZE]);
instance.name = std::move(buffer);
You can do it, but it needs some extra work. It differs depending on the type of the archive.
For text-based archives (e.g., XMLOutputArchive/XMLInputArchive and JSONOutputArchive/JSONInputArchive) you can use the saveBinaryValue() / loadBinaryValue() (http://uscilab.github.io/cereal/assets/doxygen/classcereal_1_1JSONOutputArchive.html).
Here's a complete example:
#include <iostream>
#include <memory>
#include <cereal/archives/xml.hpp>
#include <cereal/cereal.hpp>
struct Counter
{
static constexpr std::size_t BUFFER_SIZE = 12;
int index{};
std::unique_ptr<char[]> name;
Counter() = default;
Counter(int i)
: index{i},
name{new char[BUFFER_SIZE]}
{}
template<class Archive>
void load(Archive& archive)
{
archive(index);
name.reset(new char[BUFFER_SIZE]);
archive.loadBinaryValue(name.get(), BUFFER_SIZE * sizeof(decltype(name[0])));
}
template<class Archive>
void save(Archive& archive) const
{
archive(index);
archive.saveBinaryValue(name.get(), BUFFER_SIZE * sizeof(decltype(name[0])));
}
};
int main()
{
cereal::XMLOutputArchive archive(std::cout);
Counter c(42);
archive(CEREAL_NVP(c));
}
If you are using BinaryOutputArchive / BinaryInputArchive or PortableBinaryOutputArchive / PortableBinaryInputArchive the functions become saveBinary() and loadBinary() (http://uscilab.github.io/cereal/assets/doxygen/classcereal_1_1PortableBinaryOutputArchive.html).
For those, you can also wrap your array using binary_data():
template<class Archive>
void save(Archive& archive) const
{
archive(index, cereal::binary_data(name.get(), BUFFER_SIZE));
}
Related
I am trying to use boost::serialization library to serialize a class (the class A in following code) which is including a std::unique_ptr member.
Environment:
OS: Windows 10 1909
IDE: Microsoft Visual Studio Community 2019 Version 16.4.4
Library: boost-serialization 1.72.0 (installed with vcpkg tool)
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <fstream>
#include <iostream>
#include <memory>
using namespace std;
using namespace boost;
class A
{
public:
A()
{
}
A(int input_size, int input_value) // Constructor
{
this->data = std::make_unique<int[]>(input_size);
this->size = input_size;
for (int loop_number = 0; loop_number < size; loop_number++) {
data[loop_number] = input_value;
}
}
std::unique_ptr<int[]> get_data()
{
// deep copy
auto return_data = std::make_unique<int[]>(size);
for (int loop_number = 0; loop_number < size; loop_number++) {
return_data[loop_number] = data[loop_number];
}
return return_data;
}
int get_size()
{
return this->size;
}
~A()
{
}
private:
std::unique_ptr<int[]> data;
int size;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar& data;
ar& size;
}
};
int main()
{
// create and open a character archive for output
std::ofstream ofs("filename");
// create class instance
const A a_object(10, 5);
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write class instance to archive
oa << a_object;
// archive and stream closed when destructors are called
}
// ... some time later restore the class instance to its orginal state
A load_from_file;
{
// create and open an archive for input
std::ifstream ifs("filename");
boost::archive::text_iarchive ia(ifs);
// read class state from archive
ia >> load_from_file;
// archive and stream closed when destructors are called
}
return 0;
}
The compiler throws C2440 error and the error message is 'initializing': cannot convert from 'int *' to 'const T (*const )'. It refers to %includePath%\boost\serialization\unique_ptr.hpp
template<class Archive, class T>
inline void save(
Archive & ar,
const std::unique_ptr< T > &t,
const unsigned int /*file_version*/
){
// only the raw pointer has to be saved
// the ref count is rebuilt automatically on load
const T * const tx = t.get();
ar << BOOST_SERIALIZATION_NVP(tx);
}
It seems that std::unique_ptr still serialize unsuccessfully in boost-serialization library (version 1.72.0)?
Moreover, is there any solution to serialize a class with std::unique_ptr members?
Based on the fact that std::unique_ptr doesn't stored size, ar& data; in the "serialize" template function which is used for saving/loading data members can't be serialized due to the count of data is unknown in this line.
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar& data; // <== the count of data is unknown
ar& size;
}
The iterating process (as shown as the following code) of smart pointer "data" is needed in order to access all valid data.
for (int i = 0; i < size; ++i)
ar& data[i];
Therefore, the save part is like below.
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
ar& size;
for (int i = 0; i < size; ++i)
ar& data[i];
}
When it comes to load part, it is important to allocate space of smart pointer before loading data to avoid Access Violation Error
template<class Archive>
void load(Archive& ar, const unsigned int version)
{
ar& size;
data = std::make_unique<int[]>(size); // <== allocate space of smart pointer first
for (int i = 0; i < size; ++i)
ar& data[i];
}
Note: The macro BOOST_SERIALIZATION_SPLIT_MEMBER() need to be put because save/load function is split.
Finally, the serialization part in class A could be done as the following code.
friend class boost::serialization::access;
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
ar& size;
for (int i = 0; i < size; ++i)
ar& data[i];
}
template<class Archive>
void load(Archive& ar, const unsigned int version)
{
ar& size;
data = std::make_unique<int[]>(size);
for (int i = 0; i < size; ++i)
ar& data[i];
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
Reference: Boost serialization tutorial
Depending of the version of Boost you are using, you may need to implement your own non-intrusive adapter, something like this:
namespace boost {
namespace serialization {
template<class Archive, class T>
inline void save( Archive & ar, const std::unique_ptr< T > &t, const unsigned int /*file_version*/){
// only the raw pointer has to be saved
const T * const base_pointer = t.get();
ar & BOOST_SERIALIZATION_NVP(base_pointer);
}
template<class Archive, class T>
inline void load(Archive & ar,std::unique_ptr< T > &t, const unsigned int /*file_version*/){
T *base_pointer;
ar & BOOST_SERIALIZATION_NVP(base_pointer);
t.reset(base_pointer);
}
template<class Archive, class T>
inline void serialize(Archive & ar,std::unique_ptr< T > &t, const unsigned int file_version){
boost::serialization::split_free(ar, t, file_version);
}
} // namespace serialization
} // namespace boost
See this for a complete example.
I tried serialising my (neural) network and am currently stuck-ish.
The issue seems to be that you can't serialize a std::reference_wrapper. I am unsure whether I should either change the way the references to the upper nodes are stored or come up with a way to serialize those.
Are there alternatives to reference_wrappers, which I neglected and still avoid c style pointers? (which are to be avoided as far as i know)
#include <iostream>
#include <fstream>
#include <list>
#include <vector>
#include <functional>
#include <boost/archive/tmpdir.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
typedef float nodeValueType;
typedef std::pair<std::vector<nodeValueType>, std::vector<nodeValueType>> Example;
typedef std::list<Example> ExampleList;
class Node;
class Link
{
public:
Link() = delete;
Link(Node& upperNode, Node& lowerNode)
: Link(upperNode, lowerNode, 1.0e-3f * (std::rand() / (nodeValueType) RAND_MAX))
{
}
Link(Node& upperNode, Node& lowerNode, nodeValueType weight)
: weight_(weight), upperNode_(upperNode), lowerNode_(lowerNode)
{
}
Link(const Link&) = delete;
Link& operator=(const Link&) = delete;
nodeValueType weight_;
std::reference_wrapper<Node> upperNode_;
std::reference_wrapper<Node> lowerNode_;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version){
ar & this->weight_;
ar & this->upperNode_;
ar & this->lowerNode_;
}
};
class Node
{
public:
Node();
// making it hard to copy us since we really never want to move.
// we are referred in loads of pointers
// therefore moving us invalidates all of them TODO invalidation scheme?
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
void linkTo(Node& other)
{
assert(this->lowerLinks_.max_size() > (this->lowerLinks_.size() + 1) * 2);
// Link creation
this->lowerLinks_.push_back(std::shared_ptr<Link>(new Link(*this, other)));
other.upperLinks_.push_back(std::shared_ptr<Link>(this->lowerLinks_.back()));
}
std::vector<std::shared_ptr<Link>> lowerLinks_;
std::vector<std::shared_ptr<Link>> upperLinks_;
// serialization
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version){
ar & this->lowerLinks_;
}
};
int main()
{
Node n1;
Node n2;
n2.linkTo(n1);
std::string filename(boost::archive::tmpdir());
filename += "/demofile.txt";
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
oa << n1 << n2;
Node n3,n4;
// open the archive
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
// restore the schedule from the archive
ia >> n3 >> n4;
return 0;
}
There are 2 problems to overcome.
The first is that std::reference_wrapper cannot be null, so you might want to consider a 'nullable reference' type:
template<class T>
struct NullableReference
{
constexpr NullableReference() : ptr_(nullptr) {}
constexpr NullableReference(T& ref) : ptr_(std::addressof(ref)) {}
constexpr auto operator*() const -> T& { assert(!empty()); return *ptr_; }
constexpr operator T&() const { assert(!empty()); return *ptr_; }
constexpr bool empty() const { return !ptr_; }
template<class Archive>
void serialize(Archive& ar, unsigned int version)
{
ar & ptr_;
}
private:
T* ptr_;
};
The other is that Link does not have a default constructor (no doubt because of the non-nullable reference wrappers).
For this reason you may want to consider custom handling of the constructor when deserialising a Link (this is covered in the boost docs IIRC).
Of course, now that you're using a NullableReference, you can allow the default constructor for Link:
...
Link() : weight_(), upperNode_(), lowerNode_() {};
nodeValueType weight_;
NullableReference<Node> upperNode_;
NullableReference<Node> lowerNode_;
....
The code below is my current thinking to permit boost::serialization of an immutable abstract base with virtual inheritance. I hope I missed something and there is a simpler solution...?
As it stands, it raises a few questions:
Is the comment in IObject::serialize valid?
The comment in bs::save_construct_data for House seems to indicate a bug in boost::serialization. Is that correct, or is there a better way to do this?
Is there a more elegant way to deserialise Building then the Deserialise function combined with a protected constructor?
The result of (3) is that another Building implementation will necessitate a chunk of duplicated code. I suspect this will require a bit of CRTP to mitigate - any alternatives?
How does one make this work if the virtual base contains data members? I suspect this is similar (or identical) to the Building::Deserialise.
#include <fstream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/export.hpp>
namespace bs = boost::serialization;
// IBase comes from an external library, and we are not interested in
// serialising an IBase*.
class IBase {
public:
virtual ~IBase(){};
};
class IObject : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IObject's serialize\n";
// IBase contains no members so there is no need to serialise it to/from the
// archive. However, the inheritance relationship must be registered in
// boost::serialization. We cannot use base_object to do this: It will try
// to static_cast *this to IBase, but we might not have created the instance
// yet, in which case there is no virtual table and a structured exception
// will be generated.
bs::void_cast_register<IObject, IBase>(static_cast<IObject *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IObject() {}
};
class IBuilding : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IBuilding's serialize\n";
bs::void_cast_register<IBuilding, IBase>(static_cast<IBuilding *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IBuilding() {}
};
/* Tedious forward declarations to permit later friending. */
class Building;
class House;
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
template <class Archive>
inline void save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline void load_construct_data(Archive &ar, House *t,
const unsigned int version);
}
}
/* Tedious forward declarations end. */
class Building : public IBuilding, public IObject {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called Building's serialize\n";
// We can use base_object here because although the instance might not be
// created, the memory has been allocated. Since there is no virtual
// inheritance, the static_cast can succeed.
ar &bs::make_nvp("IObject", bs::base_object<IObject>(*this));
ar &bs::make_nvp("IBuilding", bs::base_object<IBuilding>(*this));
}
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
const double weight_;
protected:
const double height_;
// The Members, associated constructor, and Deserialise facilitate recreating
// this immutable base.
struct Members {
double weight_;
double height_;
};
Building(const Members &members)
: weight_(members.weight_), height_(members.height_) {}
template <class Archive> const Members Deserialise(Archive &ar) const {
double weight;
double height;
ar >> bs::make_nvp("weight_", weight) >> bs::make_nvp("height_", height);
return {weight, height};
}
public:
bool operator==(const Building &other) const {
return weight_ == other.weight_ && height_ == other.height_;
}
virtual double Height() const = 0;
};
class House : public Building {
private:
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline friend void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version);
template <class Archive>
explicit House(Archive &ar) : Building(Deserialise(ar)) {}
public:
House(double weight, double height) : Building({weight, height}) {}
virtual double Height() const { return height_; }
};
BOOST_CLASS_EXPORT(House);
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version) {
std::cout << "Called Building's save_construct_data\n";
ar << make_nvp("weight_", t->weight_) << make_nvp("height_", t->height_);
}
template <class Archive>
inline void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version) {
std::cout << "Called House's save_construct_data\n";
const auto &base = base_object<const Building>(*t);
ar << make_nvp("Building", base);
// ar << make_nvp("Building", &base); doesn't seem to work.
// Serialising out a reference calls Building's serialize method, the
// save_construct_data is only called for a pointer. This means we
// have to call it explicitly.
save_construct_data(ar, &base, version);
}
template <class Archive>
inline void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version) {
std::cout << "Called House's load_construct_data\n";
ar >> make_nvp("Building", base_object<Building>(*t));
::new (t) House{ar};
}
}
}
int main() {
const char *file_name = "house.ser";
const bool save_first = true;
const House house(45367, 2.43);
std::cout << house.Height() << "\n";
const IObject *iHouse = &house;
if (save_first) {
std::ofstream ofs(file_name);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(iHouse);
}
IBuilding *iHouse2;
{
std::ifstream ifs(file_name);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(iHouse2);
}
if (dynamic_cast<const Building &>(*iHouse) ==
dynamic_cast<const Building &>(*iHouse2))
std::cout << "ok\n";
else
std::cout << "uh oh\n";
return 0;
}
I don't believe that the comment is correct. I believe that the void_cast_register is un-necessary since IBase is known to be a virtual base class of IObject.
Futhermore, if you don't add the serialization_support free functions for IBase, you won't be able to serialise/deserialise an IBase*, only an IObject* (although I think that's fine unless you are managing ownership through the IBase rather than the IObject).
Is it possible to serialize data without serializing the type information? I ask because I'm using Cereal to serialize data into packets and sending them across the network and want to minimize the packet size as much as possible thus not including type information would help.
The only reason I think this is possible is due to the fact that on the sending and receiving end the application already knows the type information and in which order it was serialized.
It's easier to understand exactly how i'm serializing and unserializing data from my actual implementation:
NetPacket.h
#pragma once
#include <cereal\archives\portable_binary.hpp>
#include <cereal\types\polymorphic.hpp>
#include <cereal\types\memory.hpp>
#include <cereal\types\vector.hpp>
#include <cereal\types\string.hpp>
#include <sstream>
class NetVar_ {
protected:
virtual void MakeMePolymorphic() {}
public:
template <class Archive> void serialize(Archive & archive) {}
};
class NetPacket;
template <class VARTYPE> class NetVar : public NetVar_
{
public:
NetVar(void) {}
NetVar(VARTYPE Value)
{
Var = Value;
}
template <class Archive> void serialize(Archive & archive)
{
archive(Var);
}
private:
friend class NetPacket;
VARTYPE Var;
};
class NetPacket
{
private:
ENetHost* Host;
enet_uint8 ChannelNumber;
unsigned int ThisMessageID;
std::vector<std::shared_ptr<NetVar_>> PacketData;
std::string SerializePacket();
public:
NetPacket(ENetHost* iHost, const unsigned int MessageID, enet_uint8 ChannelNum);
NetPacket(const char* Data, size_t Size);
template <class DATATYPE> void WriteData(DATATYPE AddData)
{
PacketData.push_back(std::make_shared<NetVar<DATATYPE>>(AddData));
}
template <class DATATYPE> DATATYPE ReadData(const unsigned int VarNum) const
{
std::shared_ptr<NetVar_> Content = PacketData[VarNum];
std::shared_ptr<NetVar<DATATYPE>> Temp = std::static_pointer_cast<NetVar<DATATYPE>>(Content);
return Temp->Var;
}
void Send(const bool Reliable);
void Send(NetClient* Client, const bool Reliable);
unsigned int GetMessageID(void) const;
enet_uint8 GetChannelNumber(void) const;
};
NetPacket.cpp
#include <enet\enet.h>
#include "NetClient.h"
#include "NetPacket.h"
// Register Types here that you want to
// read and write to packets
CEREAL_REGISTER_TYPE(NetVar<bool>);
CEREAL_REGISTER_TYPE(NetVar<unsigned int>);
CEREAL_REGISTER_TYPE(NetVar<int>);
CEREAL_REGISTER_TYPE(NetVar<float>);
CEREAL_REGISTER_TYPE(NetVar<std::string>);
std::string NetPacket::SerializePacket()
{
std::ostringstream SData;
{
cereal::PortableBinaryOutputArchive Archive(SData);
Archive(ThisMessageID, PacketData);
}
return SData.str();
}
NetPacket::NetPacket(ENetHost* iHost, const unsigned int MessageID, enet_uint8 ChannelNum)
{
Host = iHost;
ThisMessageID = MessageID;
ChannelNumber = ChannelNum;
}
NetPacket::NetPacket(const char* Data, size_t Size)
{
std::istringstream SData(std::string(Data, Size));
{
cereal::PortableBinaryInputArchive Archive(SData);
Archive(ThisMessageID, PacketData);
}
}
void NetPacket::Send(const bool Reliable)
{
auto Out = SerializePacket();
ENetPacket* Packet;
if (Reliable)
{
Packet = enet_packet_create(Out.data(), Out.size(), ENET_PACKET_FLAG_RELIABLE);
}
else {
Packet = enet_packet_create(Out.data(), Out.size(), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
}
enet_host_broadcast(Host, ChannelNumber, Packet);
}
void NetPacket::Send(NetClient* Client, const bool Reliable)
{
auto Out = SerializePacket();
ENetPacket* Packet;
if (Reliable)
{
Packet = enet_packet_create(Out.data(), Out.size(), ENET_PACKET_FLAG_RELIABLE);
}
else {
Packet = enet_packet_create(Out.data(), Out.size(), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
}
enet_peer_send(Client->GetPeer(), ChannelNumber, Packet);
}
unsigned int NetPacket::GetMessageID(void) const
{
return ThisMessageID;
}
enet_uint8 NetPacket::GetChannelNumber(void) const
{
return ChannelNumber;
}
As you can see from ReadData and WriteData defined in NetPacket.h, to read and write data you are required to specify the type information into the template function. Due to this requirement, type info is known on the reader and the writers end and in which order it was written. Is there any way to omit type info when serializing the data?
I apologize for the large question and I very much appreciate your time on this matter.
I need the ability to save/read my data structures in my project, but all the data is in the form of quite complicated and distinct structures themselves which I typically implement through other structs and vectors. I have wrapped all of them up into a single struct so that I have something like
struct master{
std::vector<apprentice_type1> a;
std::vector<apprentice_type2> b; //etc.
std::string label;
};
with other ones defined like
struct apprentice_type1{
vec location;
int point_label;
std::vector<int> relational_data;
};
struct vec{
double x,y,z;
};
So it gets pretty complicated! I was desperately hoping something nice, quick and naive like
master obj;
//write to obj....
std::ofstream ofs("data.dat", std::ios::binary);
ofs.write((char *)&obj, sizeof(obj));
would work, but at present it doesn't seem to. Before I get lost in the debugging rabbit hole is this actually possible the way I'm approaching it or do I need to rethink? If so, how?
Thanks.
If you want an alternative to Boost serialization and have access to a C++11 compiler, you can also check out cereal. It works in a near identical fashion to Boost serialize but is a header only library so there is nothing to link against.
[...] or do I need to rethink? If so, how?
You will probably need to provide a full implementation (i.e. explore the "rabbit-hole").
This is a known problem (stream serialization) and there is no single best-approach solution to it, because most implementations need to solve different needs.
You can do one of the following:
implement std::i/ostream serialization; This means you will go over your classes and implement operator>>(std::istream*, your_type&) and it's inverse operator<<(std::ostream*, your_type&).
implement serialization based on a stream library (like boost.archive).
use a JSON or XML library.
use google protocol buffers (or something else)
roll your own implementation, depending on your needs.
If you go for boost::serialization, here is a little sample:
#include <fstream>
#include <vector>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/vector.hpp>
template <typename T>
inline const boost::serialization::nvp<T> archive_value(const char* name, T& value) {
return boost::serialization::make_nvp(name, value);
}
const unsigned Version = 0;
class Point{
public:
double x,y,z;
private:
template <typename P, typename Archive>
static void serialize(P& p, Archive& ar, const unsigned int version) {
std::cout << "Point\n";
ar & archive_value("X", p.x);
ar & archive_value("Y", p.y);
ar & archive_value("Z", p.z);
}
public:
template <typename Archive>
void serialize(Archive& ar, const unsigned int version) const {
serialize(*this, ar, version);
}
template <typename Archive>
void serialize(Archive& ar, const unsigned int version) {
serialize(*this, ar, version);
}
};
BOOST_CLASS_VERSION(Point, Version)
struct Scene{
std::vector<Point> points;
private:
template <typename S, typename Archive>
static void serialize(S& s, Archive& ar, const unsigned int version) {
std::cout << "Scene\n";
ar & archive_value("Points", s.points);
}
public:
template <typename Archive>
void serialize(Archive& ar, const unsigned int version) const {
serialize(*this, ar, version);
}
template <typename Archive>
void serialize(Archive& ar, const unsigned int version) {
serialize(*this, ar, version);
}
};
BOOST_CLASS_VERSION(Scene, Version)
template <typename Archive>
void register_types(Archive& ar) {
ar.template register_type<Point>();
ar.template register_type<Scene>();
}
int main() {
Scene scene;
scene.points = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 } };
// Output
{
std::ofstream out("test.dat", std::ios_base::binary);
boost::archive::binary_oarchive output(out);
// First the version!
output & archive_value("Version", Version);
// Next the types!
register_types(output);
// Finally the data
output & archive_value("Scene", scene);
}
scene.points = {};
// Input
{
int version;
std::ifstream in("test.dat", std::ios_base::binary);
boost::archive::binary_iarchive input(in);
// First the version!
input & archive_value("Version", Version);
// Next the types!
register_types(input);
// Finally the data
input & archive_value("Scene", scene);
}
for(const auto& p : scene.points)
std::cout << p.x << '\n';
}
Note: The file format may evolve and serialization functions (input and/or output) may get adjustment depending on the file version.