hello world tutorial complete

This commit is contained in:
Mindaugas Vinkelis
2017-08-14 22:43:51 +03:00
parent 6e8898317e
commit a1104a9b95
7 changed files with 182 additions and 8 deletions

View File

@@ -18,6 +18,12 @@ All cross-platform requirements are enforced at compile time, so serialized data
* Easy to extend for types that requires different serialization and deserialization logic (e.g. pointers, or geometry compression).
* Error checking at runtime on deserialization, and asserts on serialization errors.
## How to use it
This documentation comprises these parts:
* [Tutorial](doc/tutorial/README.md) - getting started.
* [Reference section](doc/README.md) - all the details.
## Example
```cpp
#include <bitsery/bitsery.h>

View File

@@ -2,9 +2,11 @@ To get the most out of **Bitsery**, start with the [tutorial](tutorial/README.md
Once you're familiar with the library consider the following reference material.
Library design:
* [*valueN* instead of *value*](design/func_n.md)
* [fundamental types](design/fundamental_types.md)
* [serializer/deserializer functions overloads](design/function_overload.md)
* [extending library functionality](design/extensions.md)
* [errors handling](design/errors.md)
Serializer/Deserializer functions (alphabetical order):
* [align](fnc_array.md)
@@ -35,4 +37,4 @@ FAQ:
Other:
* [Why Bitsery?](why-bitsery.md)
* [Contributing](../CONTRIBUTING.md)
* [Change log](../CHANGELOG.md)
* [Change log](../CHANGELOG.md)

1
doc/design/errors.md Normal file
View File

@@ -0,0 +1 @@
errors handling design

1
doc/design/function_n.md Normal file
View File

@@ -0,0 +1 @@
valueN instead of value

View File

@@ -2,8 +2,13 @@ The grand plan for this tutorial is to learn how to serialize/deserialize any ob
This tutorial will cover these main topics:
* [Hello World](hello_world.md) how to set up and to serialize a [fundamental value](../design/fundamental.md).
* [Hello World](hello_world.md) how to serialize a simple struct.
* [2 in 1](two_in_one.md) how to write one control flow for both, serialization and deserialization.
* [Squeeze Me!](compression.md) how to compress your data if you know what it stores.
* [Anything is Possible](extensions.md) how to extend library for your custom container, compress geometry and more.
* [Little or Big](endianness.md) how to change Endianness if you want best performance on PowerPC
* [Little or Big](endianness.md) how to change Endianness if you want best performance on PowerPC.
In order to successfully use the library you need c++14 compatible compiler. In theory you could also use c++11 compatible compiler, but c++14 generic lambdas really change the way you can work with this library, so all tutorial sections will asume that you use c++14 compatible compiler.
So without further ado lets start with [hello world](hello_world.md).

View File

@@ -0,0 +1,163 @@
# The Problem
You want to serialize *Player* structure efficiently into buffer.
```cpp
struct Vector3f {
float x;
float y;
float z;
};
struct Player {
Vector3f pos;
char name[50];
};
```
# Poor man's implementation
Since you don't want to waste any space using any text serialization library like json or xml, the one of the most easiest and obvious solution is to simply write memory representation of the structure directly to buffer.
Possible implementation could look like this.
```cpp
#include <iostream>
#include <vector>
#include <cassert>
#include <cstdint>
#include <algorithm>
struct Vector3f {
float x;
float y;
float z;
};
struct Player {
Vector3f pos;
char name[50];
};
void serialize(std::vector<uint8_t>& buf, const Player& data) {
auto ptr = reinterpret_cast<const uint8_t*>(&data);
std::copy_n(ptr, sizeof(data), std::back_inserter(buf));
}
int main() {
Player data;
std::vector<uint8_t> buf;
serialize(buf, data);
assert(buf.size() == sizeof(data));
std::cout << "size: " << buf.size() << std::endl;
return 0;
}
```
```bash
size: 64
```
Although it is simple and fast (it could be faster if we reserve buffer before writing) it has a lot of limitations.
* char[50] always writes to atleast 50 bytes in buffer, even if player name is *Yolo*.
* you can't replace char[50] with std::string.
* you can't use this solution if you need to support different endianness systems, and you should be extra careful if different systems has different size for fundamental types like int, long int, etc...
* you pay for structure field alignment hence size is equal to 64, not 62(3*4+50).
You can improve your name serialization in various ways, but then your serialization and deserialization code gets compllicated and error prone. We can do better than this.
# **Bitsery** solution
Let's solve the same problem with the library.
```cpp
#include <vector>
#include <bitsery/bitsery.h>
#include <cstring>
#include <iostream>
struct Vector3f {
float x;
float y;
float z;
};
struct Player {
Vector3f pos;
char name[50];
};
using namespace bitsery;
void serialize(Serializer<BufferWriter>& s, const Player& data) {
s.value4(data.pos.x);
s.value4(data.pos.y);
s.value4(data.pos.z);
s.text1(data.name);
}
int main() {
Player data;
strcpy(data.name,"Yolo");
std::vector<uint8_t> buf;
BufferWriter bw{buf};
Serializer<BufferWriter> ser{bw};
serialize(ser, data);
bw.flush();
std::cout << "size: " << buf.size() << std::endl;
return 0;
}
```
```bash
size: 17
```
First of all, buffer size dropped from 64 down to 17bytes: 12 bytes (3*4) for floats and only 5bytes for the name "Yolo".
In the process you also lost all limitations that had naive solution. You even gain some features for free:
* endianess support.
* much more readable structure serialization code.
Let's take a look at the code, how we did this.
There are three distinct parts that participate in serialization process.
* Buffer - container that we store our serialized data, in our case vector<uint8_t>.
* BufferWriter - resposible for writing bytes and bits to *Buffer*, it also makes sure that it is portable across Little and Big endian systems.
* Serializer - extendable interface that converts any type to bytes or bits, and use *BufferWriter* to write them. Serializer interface also ensures that code is portable at compile time. This means, that if your serialization code compiles on other platform, it will be 100% correct.
```cpp
std::vector<uint8_t> buf;
BufferWriter bw{buf};
Serializer<BufferWriter> ser{bw};
```
Serialization function is very readable, and explicitly express intent what and how to serialize:
* *value4* serialize [fundamental type](../design/fundamental_types.md) (ints, floats, chars, enums) of 4 bytes.
* *text1* effectively serialize text, and underlying text type is 1byte per letter.
```cpp
s.value4(data.pos.x);
s.value4(data.pos.y);
s.value4(data.pos.z);
s.text1(data.name);
```
> learn more about why you need to write [value4 instead of value](../design/function_n.md).
Finally before sending buffer to network you must *flush* BufferWriter, it writes any remaining bits to buffer. In our case it is not required, because we only worked with whole bytes, but it is good practice to always use it after finishing serialization.
```cpp
bw.flush();
```
# Summary
You have learned how to serialize simple structure to buffer that occupies no unnecessary bytes, and is portable across any system. You also learned that serialization process consist of three independant parts: buffer, buffer writer, and serializer. You used serializer to explicitly declare what and how to serialize and learned that you should always call BufferWriter.flush() before using buffer data.
In [next chapter](two_in_one.md) you'll learn how to use this expressive, declarative serialization function and use it to deserialize buffer to object, and in the process gain runtime error checking for free!

View File

@@ -27,15 +27,11 @@ struct MyStruct {
uint32_t i;
MyEnum e;
std::vector<float> fs;
char16_t x;
bool y;
};
//define how object should be serialized/deserialized
SERIALIZE(MyStruct) {
return s.
value1(o.y).
value2(o.x).
value4(o.i).
value2(o.e).
container4(o.fs, 10);
@@ -71,4 +67,4 @@ int main() {
//deserialize same object, can also be invoked like this: serialize(des, data)
des.object(res);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}
}