mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 08:13:56 +00:00
VIP tutorial on Version extension
This commit is contained in:
@@ -22,12 +22,10 @@ void serialize(S& s, MyStruct& o) {
|
||||
s.container4b(o.fs, 10);//resizable containers also requires maxSize, to make it safe from buffer-overflow attacks
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -39,12 +37,12 @@ int main() {
|
||||
//use quick serialization function,
|
||||
//it will use default configuration to setup all the nesessary steps
|
||||
//and serialize data to container
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
//same as serialization, but returns deserialization state as a pair
|
||||
//first = error code, second = is buffer was successfully read from begin to the end.
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -41,12 +41,10 @@ namespace MyTypes {
|
||||
}
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -55,10 +53,10 @@ int main() {
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
MyTypes::Monster res{};
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,10 @@ struct MyStruct {
|
||||
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -37,10 +35,10 @@ int main() {
|
||||
|
||||
//serialization, deserialization flow is unchanged as in basic usage
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -29,27 +29,24 @@ void serialize(S& s, MyStruct& o) {
|
||||
using MyTuple = std::tuple<float, MyStruct>;
|
||||
using MyVariant = std::variant<int64_t, MyTuple, MyStruct>;
|
||||
|
||||
// for convenience
|
||||
using namespace bitsery;
|
||||
|
||||
// define default serialize function for MyVariant, so that we could use quickSerialization/Deserialization functions
|
||||
template<typename S>
|
||||
void serialize(S& s, MyVariant& o) {
|
||||
// in order to serialize a variant, it needs to know how to do it for all types
|
||||
// we can do this simply by providing any callable object, that accepts serializer and type as arguments
|
||||
s.ext(o, ext::StdVariant{
|
||||
s.ext(o, bitsery::ext::StdVariant{
|
||||
// specify how to serialize tuple by creating a lambda
|
||||
[](S& s, MyTuple& o) {
|
||||
// StdTuple is used exactly the same as StdVariant
|
||||
s.ext(o, ext::StdTuple{
|
||||
s.ext(o, bitsery::ext::StdTuple{
|
||||
// this is convenient callable object to specify integral value size
|
||||
// it is different equivalent to lambda [](auto& s, float&o) { s.value4b(o);}
|
||||
ext::OverloadValue<float, 4>{},
|
||||
bitsery::ext::OverloadValue<float, 4>{},
|
||||
// it is not required to provide MyStruct overload, because it we have defined 'serialize' function for it
|
||||
});
|
||||
},
|
||||
// this might also be useful if you want to overload using extension
|
||||
ext::OverloadExtValue<int64_t, 8, ext::CompactValue>{},
|
||||
bitsery::ext::OverloadExtValue<int64_t, 8, bitsery::ext::CompactValue>{},
|
||||
// you can even go further and instead of writing lambda for MyTuple you can as well compose the same functionality
|
||||
// with OverloadExtObject, like this:
|
||||
// (comment out MyTuple lambda, and uncomment this)
|
||||
@@ -59,7 +56,7 @@ void serialize(S& s, MyVariant& o) {
|
||||
[](S& s, MyStruct& o) {
|
||||
s.value4b(o.f);
|
||||
s.container(o.v, 1000, [](S& s, int32_t& v) {
|
||||
s.ext4b(v, ext::CompactValue{});
|
||||
s.ext4b(v, bitsery::ext::CompactValue{});
|
||||
});
|
||||
},
|
||||
// NOTE.
|
||||
@@ -78,8 +75,8 @@ void serialize(S& s, MyVariant& o) {
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -93,13 +90,13 @@ int main() {
|
||||
//use quick serialization function,
|
||||
//it will use default configuration to setup all the nesessary steps
|
||||
//and serialize data to container
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
//same as serialization, but returns deserialization state as a pair
|
||||
//first = error code, second = is buffer was successfully read from begin to the end.
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data == res);
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -67,10 +67,9 @@ using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using namespace bitsery;
|
||||
// define adapter types,
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -90,10 +89,10 @@ int main() {
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = quickSerialization(ctx, OutputAdapter{buffer}, data);
|
||||
auto writtenSize = bitsery::quickSerialization(ctx, OutputAdapter{buffer}, data);
|
||||
|
||||
MyTypes::GameState res{};
|
||||
auto state = quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ void serialize(S& s, MyStruct& o) {
|
||||
s.value8b(o.f);
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, 0.045};
|
||||
@@ -35,7 +33,7 @@ int main() {
|
||||
}
|
||||
|
||||
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
|
||||
Serializer<OutputBufferedStreamAdapter> ser{s};
|
||||
bitsery::Serializer<bitsery::OutputBufferedStreamAdapter> ser{s};
|
||||
ser.object(data);
|
||||
//flush to writer
|
||||
ser.adapter().flush();
|
||||
@@ -50,8 +48,8 @@ int main() {
|
||||
|
||||
//same as serialization, but returns deserialization state as a pair
|
||||
//first = error code, second = is buffer was successfully read from begin to the end.
|
||||
auto state = quickDeserialization<InputStreamAdapter>(s, res);
|
||||
auto state = bitsery::quickDeserialization<bitsery::InputStreamAdapter>(s, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.f == res.f && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -68,12 +68,10 @@ namespace MyTypes {
|
||||
}
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -84,11 +82,11 @@ int main() {
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//since we're using different configuration, we cannot use quickSerialization function.
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
MyTypes::Monster res{};
|
||||
//deserialize
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -78,13 +78,10 @@ namespace bitsery {
|
||||
struct SelectSerializeFnc<MultipleInheritance>:UseNonMemberFnc {};
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -95,13 +92,13 @@ int main() {
|
||||
|
||||
Buffer buf{};
|
||||
|
||||
ext::InheritanceContext ctx1;
|
||||
auto writtenSize = quickSerialization(ctx1, Writer{buf}, data);
|
||||
bitsery::ext::InheritanceContext ctx1;
|
||||
auto writtenSize = bitsery::quickSerialization(ctx1, Writer{buf}, data);
|
||||
assert(writtenSize == 4);//base is serialized once, because it is inherited virtually
|
||||
|
||||
MultipleInheritance res{0};
|
||||
ext::InheritanceContext ctx2;
|
||||
auto state = quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
bitsery::ext::InheritanceContext ctx2;
|
||||
auto state = bitsery::quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -49,16 +47,16 @@ int main() {
|
||||
|
||||
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
|
||||
//create writer and serialize container
|
||||
Serializer<Writer> ser{buffer};
|
||||
bitsery::Serializer<Writer> ser{buffer};
|
||||
ser.container(data, 10);
|
||||
ser.adapter().flush();
|
||||
|
||||
//create reader and deserialize container
|
||||
Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
|
||||
bitsery::Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
|
||||
des.container(res, 10);
|
||||
|
||||
//check if everything went ok
|
||||
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
assert(res == data);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,12 +78,10 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
//we will need PointerLinkingContext to work with pointers
|
||||
//if we would require additional context for our own custom flow, we can define it as tuple like this:
|
||||
@@ -114,7 +112,7 @@ int main() {
|
||||
size_t writtenSize{};
|
||||
//in order to use pointers, we need to pass pointer linking context serializer/deserializer
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
bitsery::ext::PointerLinkingContext ctx{};
|
||||
writtenSize = quickSerialization(ctx, Writer{buffer}, data);
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
@@ -125,10 +123,10 @@ int main() {
|
||||
|
||||
Test1Data res{};
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
bitsery::ext::PointerLinkingContext ctx{};
|
||||
auto state = quickDeserialization(ctx, Reader{buffer.begin(), writtenSize}, res);
|
||||
//check if everything went find
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(state.first == bitsery::ReaderError::NoError && state.second);
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
|
||||
@@ -181,23 +181,21 @@ namespace bitsery {
|
||||
// also it automatically ensures, that classes is registered in the same order for serialization and deserialization
|
||||
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
|
||||
|
||||
|
||||
//use bitsery namespace for convenience
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
|
||||
//we need to define few things in order to work with polymorphism
|
||||
//1) we need pointer linking context to work with pointers
|
||||
//2) we need polymorphic context to be able to work with polymorphic types
|
||||
using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<ext::StandardRTTI>>;
|
||||
using TContext = std::tuple<
|
||||
bitsery::ext::PointerLinkingContext,
|
||||
bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
|
||||
//NOTE:
|
||||
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
|
||||
using MySerializer = Serializer<Writer, TContext>;
|
||||
using MyDeserializer = Deserializer<Reader, TContext>;
|
||||
using MySerializer = bitsery::Serializer<Writer, TContext>;
|
||||
using MyDeserializer = bitsery::Deserializer<Reader, TContext>;
|
||||
|
||||
//checks if deserialized data is equal
|
||||
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
|
||||
@@ -257,7 +255,7 @@ int main() {
|
||||
//deserialize our data
|
||||
MyDeserializer des{ctx, buffer.begin(), writtenSize};
|
||||
des.object(res);
|
||||
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
// clear shared state from pointer linking context,
|
||||
|
||||
Reference in New Issue
Block a user