You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*[Building and running tests](#building-and-running-tests)
36
41
*[3rd party](#3rd-party)
37
42
*[License](#license)
@@ -50,20 +55,21 @@ The source and header files inside the `src/` directory are only tests and shoul
50
55
# Usage
51
56
The library has a global header file ([`bitstream/bitstream.h`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/bitstream.h)) which includes every other header file in the library.
52
57
53
-
If you only need certain features, you can simply include the files you need.
58
+
If you only need certain features you can instead opt to just include the files you need.
54
59
The files are stored in categories:
55
60
*[`quantization/`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/quantization/) - Files relating to quantizing floats and quaternions into fewer bits
56
61
*[`stream/`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/stream/) - Files relating to streams that read and write bits
57
62
*[`traits/`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/traits/) - Files relating to various serialization traits, like serializble strings, integrals etc.
58
63
59
64
An important aspect of the serialiaztion is performance, since the library is meant to be used in a tight loop, like with networking.
60
65
This is why most operations don't use exceptions, but instead return true or false depending on whether the operation was a success.
61
-
It's important to check these return values after every operation, especially when reading.
66
+
It's important to check these return values after every operation, especially when reading from an unknown source.
62
67
You can check it manually or use the `BS_ASSERT(x)` macro for this, if you want your function to return false on failure.
63
68
64
69
It is also possible to dynamically put a break point or trap when a bitstream would have otherwise returned false. This can be great for debugging custom serialization code, but should generally be left out of production code. Simply `#define BS_DEBUG_BREAK` before including any of the library header files if you want to break when an operation fails.
65
70
66
-
For more examples of usage, see the [Serialization Examples](#serialization-examples) below.
71
+
For more concrete examples of usage, see the [Serialization Examples](#serialization-examples) below.
72
+
If you need to add your own serializable types you should also look at the [Extensibility](#extensibility) section.
67
73
You can also look at the unit tests to get a better idea about what you can expect from the library.
68
74
69
75
# Documentation
@@ -350,6 +356,8 @@ The library is made with extensibility in mind.
350
356
The `bit_writer` and `bit_reader` use a template trait specialization of the given type to deduce how to serialize and deserialize the object.
351
357
The only requirements of the trait is that it has (or can deduce) 2 static functions which take a bit_writer& and a bit_reader& respectively as their first argument.
352
358
The 2 functions must also return a bool indicating whether the serialization was a success or not, but can otherwise take any number of additional arguments.
359
+
360
+
## Adding new serializables types
353
361
The general structure of a trait looks like the following:
354
362
355
363
```cpp
@@ -366,27 +374,35 @@ struct serialize_traits<TRAIT_TYPE> // The type to use when referencing this spe
366
374
};
367
375
```
368
376
369
-
Note that `TRAIT_TYPE` does not necessarily have to be part of the serialize function definitions.
370
-
It is purely used to specify which trait to use when serializing, since it cannot be deduced from the arguments.<br/>
371
-
To use the trait above to serialize an object you need to explicitly specify it:
372
-
```cpp
373
-
bool status = writer.serialize<TRAIT_TYPE>(...);
374
-
```
375
-
376
-
The specialization can also be unified with templating, if writing and reading look similar:
377
+
## Unified serialization
378
+
The serialization can also be unified with templating, if writing and reading look similar.
379
+
If some parts of the serialization process don't match entirely you can query the `Stream::reading` or `Stream::writing` and branch depending on the value.
380
+
An example of this can be seen below:
377
381
```cpp
378
382
template<>
379
383
struct serialize_traits<TRAIT_TYPE> // The type to use when serializing
380
384
{
381
385
// Will be called when writing or reading the object to a stream
382
386
template<typename Stream>
383
387
static bool serialize(Stream& stream, ...)
384
-
{ ... }
388
+
{
389
+
// Some code that looks the same for writing and reading
390
+
391
+
if constexpr (Stream::writing) {
392
+
// Code that should only be run when writing
393
+
}
394
+
395
+
// A variable that differs if the stream is writing or reading
396
+
int value = Stream::reading ? 0 : 1;
397
+
398
+
...
399
+
}
385
400
};
386
401
```
387
402
403
+
## Partial trait specializations
388
404
The specialization can also be templated to work with a number of types.
389
-
It also works with `enable_if`:
405
+
It also works with `enable_if` as the second argument:
390
406
```cpp
391
407
// This trait will be used by any non-const integral pointer type (char*, uint16_t* etc.)
392
408
template<typename T>
@@ -398,6 +414,41 @@ bool status = writer.serialize<int16_t*>(...);
398
414
bool status = writer.serialize<const int16_t*>(...);
399
415
```
400
416
417
+
Note that `TRAIT_TYPE` does not necessarily have to be part of the serialize function definitions.
418
+
It can just be used to specify which trait to use when serializing, if it cannot be deduced from the arguments.<br/>
419
+
Below is an example where we serialize an object by explicitly defining the trait type:
420
+
```cpp
421
+
bool status = writer.serialize<TRAIT_TYPE>(...);
422
+
```
423
+
424
+
## Trait deduction
425
+
When calling the `serialize` function on a `bit_writer` or `bit_reader`, the trait can sometimes be deduced instead of being explicitly declared.
426
+
This can only be done if the type of the second argument in the `static bool serialize(...)` function is (roughly) the same as the trait type.
427
+
An example of the structure for an implicit trait can be seen below:
428
+
```cpp
429
+
template<>
430
+
structserialize_traits<TRAIT_TYPE> // The type to use when referencing this specific trait
431
+
{
432
+
// The second argument is the same as TRAIT_TYPE (const and lvalue references are removed when deducing)
The above trait could then be used when implicitly serializing an object of type `TRAIT_TYPE`:
443
+
```cpp
444
+
TRAIT_TYPE value;
445
+
bool status = writer.serialize(value, ...);
446
+
```
447
+
448
+
It doesn't work on all types, and there is some guesswork involved relating to const qualifiers.
449
+
E.g. a trait of type `char` is treated the same as `const char&` and thus the call would be ambiguous if both had a trait specialization.
450
+
In case of ambiguity you will still be able to declare the trait explicitly when calling the `serialize` function.
451
+
401
452
More concrete examples of traits can be found in the [`traits/`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/traits/) directory.
0 commit comments