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
@@ -348,11 +354,15 @@ These examples can also be seen in [`src/test/examples_test.cpp`](https://github
348
354
# Extensibility
349
355
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.
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.
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
351
361
The general structure of a trait looks like the following:
352
362
353
363
```cpp
354
364
template<>
355
-
structserialize_traits<TRAIT_TYPE> // The type to use when serializing
365
+
structserialize_traits<TRAIT_TYPE> // The type to use when referencing this specific trait
356
366
{
357
367
// Will be called when writing the object to a stream
358
368
static bool serialize(bit_writer& stream, ...)
@@ -364,36 +374,81 @@ struct serialize_traits<TRAIT_TYPE> // The type to use when serializing
364
374
};
365
375
```
366
376
367
-
Note that `TRAIT_TYPE` does not necessarily have to be part of the serialize function definitions.
368
-
It is purely used to specify which trait to use when serializing, since it cannot be deduced from the arguments.<br/>
369
-
To use the trait above to serialize an object you need to explicitly specify it:
370
-
```cpp
371
-
bool status = writer.serialize<TRAIT_TYPE>(...);
372
-
```
373
-
374
-
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:
375
381
```cpp
376
382
template<>
377
383
struct serialize_traits<TRAIT_TYPE> // The type to use when serializing
378
384
{
379
385
// Will be called when writing or reading the object to a stream
380
386
template<typename Stream>
381
387
static bool serialize(Stream& stream, ...)
382
-
{ ... }
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
+
}
383
400
};
384
401
```
385
402
403
+
## Partial trait specializations
386
404
The specialization can also be templated to work with a number of types.
387
-
It also works with `enable_if`:
405
+
It also works with `enable_if` as the second argument:
388
406
```cpp
389
-
// This trait will be used by any integral pointer type (char*, uint16_t* etc.)
407
+
// This trait will be used by any non-const integral pointer type (char*, uint16_t* etc.)
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
+
397
452
More concrete examples of traits can be found in the [`traits/`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/traits/) directory.
static_assert(utility::has_serialize_v<Trait, bit_reader, Args...>, "Could not find serializable trait for the given type. Remember to specialize serializable_traits<> with the given type");
static_assert(utility::has_serialize_v<deduce_t, bit_reader, Trait, Args...>, "Could not deduce serializable trait for the given arguments. Remember to specialize serializable_traits<> with the given type");
0 commit comments