mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
106 lines
3.1 KiB
C++
106 lines
3.1 KiB
C++
#include "Tutorial.h"
|
|
|
|
void Serialize()
|
|
{
|
|
/// .Binary serialization
|
|
|
|
/// Serialization is a mechanism that converts structured data to/from binary stream. In
|
|
/// U++, loading and storing of data is performed by single code, in most cases represented
|
|
/// by method `Serialize`. Serialization is performed directly with basic `Stream`. To this
|
|
/// end, `Stream` features a single boolean representing the direction of serialization
|
|
/// process. The direction can be checked using `IsLoading` and `IsStoring` methods and
|
|
/// changed with `SetStoring` and `SetLoading` methods. Direction is usually set properly
|
|
/// by derived classes (e.g. FileOut sets it to storing, FileIn to loading).
|
|
///
|
|
/// Shortcut to calling `Serialize` method is `operator%`, which is templated overload that
|
|
/// calls `Serialize` for given variable (primitive types have direct overload in `Stream` class):
|
|
|
|
StringStream ss;
|
|
|
|
int x = 123;
|
|
Color h = White();
|
|
|
|
ss % x % h;
|
|
|
|
StringStream ss2(ss.GetResult());
|
|
|
|
int x2;
|
|
Color h2;
|
|
|
|
ss2 % x2 % h2;
|
|
|
|
DUMP(x2);
|
|
DUMP(h2);
|
|
|
|
/// When serialization fails to load the data (e.g. because of wrong structure or not
|
|
/// enough data in the stream), `Stream::LoadError` is invoked, which can trigger the
|
|
/// exception if the stream is `LoadThrowing`:
|
|
|
|
ss2.Seek(0);
|
|
ss2.LoadThrowing();
|
|
try {
|
|
ss2 % x2 % h2 % x2;
|
|
}
|
|
catch(LoadingError) {
|
|
LOG("Deserialization has failed");
|
|
}
|
|
|
|
/// Examples so far serve mostly like basic demonstration of serialization. In practice,
|
|
/// the implementation is usually represented by `Serialize` method of class that is to be
|
|
/// compatible with this concept. To that end, it is a good idea to provide means for
|
|
/// future expansion of such class:
|
|
|
|
|
|
struct MyFoo {
|
|
int number;
|
|
Color color;
|
|
|
|
void Serialize(Stream& s) {
|
|
int version = 0;
|
|
s / version; // allow backward compatibility in the future
|
|
s.Magic(31415); // put magic number into the stream to check for invalid data
|
|
s % number % color;
|
|
}
|
|
};
|
|
|
|
MyFoo foo;
|
|
foo.number = 321;
|
|
foo.color = Blue();
|
|
|
|
/// `StoreAsFile`, `StoreAsString`, `LoadFromFile` and `LoadFromString` are convenience
|
|
/// functions that simplify storing / loading objects to / from the most common forms of
|
|
/// storage:
|
|
|
|
String data = StoreAsString(foo);
|
|
MyFoo foo2;
|
|
LoadFromString(foo2, data);
|
|
DUMP(foo2.number);
|
|
DUMP(foo2.color);
|
|
|
|
/// Now if `MyFoo` was to be extended to `MyFoo2` and we wanted to maintain the ability to
|
|
/// load it from binary data stored by original `MyFoo`, we can branch on previously stored
|
|
/// `version`:
|
|
|
|
struct MyFoo2 {
|
|
int number;
|
|
Color color;
|
|
String text;
|
|
|
|
void Serialize(Stream& s) {
|
|
int version = 1;
|
|
s / version;
|
|
s % number % color;
|
|
if(version >= 1)
|
|
s % text;
|
|
}
|
|
};
|
|
MyFoo2 foo3;
|
|
LoadFromString(foo3, data);
|
|
DUMP(foo3.number);
|
|
DUMP(foo3.color);
|
|
|
|
/// Note: `operator/` is Stream method with several overloads optimized for small value -
|
|
/// in this case `int` is stored as single byte if possible (and as 5 bytes if not).
|
|
|
|
///
|
|
}
|