18 Commits

Author SHA1 Message Date
Mindaugas Vinkelis
ddca8e4ad0 extensions for tuple, variant and chrono types 2019-03-12 14:54:04 +02:00
Mindaugas Vinkelis
1fe2b398fc Merge pull request #12 from ArekSredzki/fix-quickMeasureSize
Fix quickMeasureSize<T>() compilation.
2019-01-30 11:49:37 +02:00
Arek Sredzki
574ec69cca Fix quickMeasureSize<T>() compilation. 2019-01-30 01:44:34 -08:00
Mindaugas Vinkelis
8e94596a6f Merge pull request #11 from ArekSredzki/misc-documentation-improvements
Fix various grammatical and spelling mistakes within the docs
2019-01-18 14:37:59 +02:00
Arek Sredzki
fac2c8a7ce Fix various grammatical and spelling mistakes within the docs 2019-01-17 10:31:58 -08:00
Mindaugas
a6dad0885f visual studio variadic templates issues 2019-01-16 11:27:56 +02:00
Mindaugas
65f90637df input buffer adapter accepts const buffer 2019-01-10 20:48:03 +02:00
Mindaugas
b10f86da00 non default constructible types 2019-01-10 19:08:15 +02:00
Mindaugas
6c3e1aee43 removed anonymous namespace from PolymorphicBaseClass as it only works
on clang, and is not standard compliant
2019-01-08 16:00:02 +02:00
Mindaugas
e5f8d5742f Merge branch 'master' of https://github.com/fraillt/bitsery 2019-01-08 15:08:27 +02:00
Mindaugas
a2ecf8d7b0 polymorphism improvements and new CompactValue extension 2019-01-08 15:06:29 +02:00
Mindaugas Vinkelis
670130397b Merge pull request #6 from AJIOB/master
VS 2017.5.6 example project compilation fix
2018-09-17 09:47:36 +03:00
AJIOB
4a0b3cae98 VS 2017.5.6 example compilation fix 2018-09-15 08:37:56 +03:00
Mindaugas Vinkelis
b3b32ab393 Merge pull request #5 from YarikTH/patch-1
Update smart_pointers_with_polymorphism.cpp
2018-08-27 07:24:22 +03:00
Yaroslav
6ebdb9915b Update smart_pointers_with_polymorphism.cpp
Fix Color::operator == in smart_pointers_with_polymorphism example
2018-08-24 10:09:17 +03:00
Mindaugas
2e62bd08e3 cleanup 2018-08-23 14:57:48 +03:00
Mindaugas
54f69a5eea polymorphism and smart pointers 2018-08-23 14:44:58 +03:00
Mindaugas Vinkelis
275c4138ee polymorphism in progress 2018-08-20 13:10:10 +03:00
95 changed files with 4564 additions and 978 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.idea/
.vs/
build/
cmake-build-debug/
cmake-build-*
CTestConfig.cmake
Testing/

View File

@@ -1,3 +1,104 @@
# [4.6.0](https://github.com/fraillt/bitsery/compare/v4.5.1...v4.6.0) (2019-03-12)
### Features
* new extensions **StdTuple** and **StdVariant** for `std::tuple` and `std::variant`. These are the first extensions that requires C++17, or higher, standard enabled.
Although `std::tuple` is C++11 type, but from usage perspective it has exactly the same requirements as `std::variant` and relies heavily on having class template argument deduction guides to make it convenient to use.
You can easily use `std::tuple` without any extension at all, so the main motivation was to create convenient interface for **StdVariant** and use the same interface for **StdTuple** as well.
* instead of providing custom lambda to overload each type in tuple or variant, there was added several helper callable objects.
**OverloadValue** wrapper around `s.value<N>(o)`, **OverloadExtValue** wrapper around `s.ext<N>(o, Ext{})` and **OverloadExtObject** wrapper around `s.ext(o, Ext{})`.
* new extensions **StdDuration** and **StdTimePoint** for `std::chrono::duration` and `std::chrono::time_point`.
### Improvements
tests now uses `gtest_discover_tests` function, to automatically discover tests, which requires CMake 3.10.
# [4.5.1](https://github.com/fraillt/bitsery/compare/v4.5.0...v4.5.1) (2019-01-16)
### Improvements
* template specializations, where possible, was changed to avoid using variadics, some Visual Studio compilers has [issues](https://developercommunity.visualstudio.com/content/problem/3437/error-with-c11-variadics.html) with variadic templates.
* reduced compile warnings for VisualStudio:
* added explicit casts
* renamed `struct` to `class` where class is used as friend. e.g. `friend class bitsery::Access`, because it is more conventional usage.
# [4.5.0](https://github.com/fraillt/bitsery/compare/v4.4.0...v4.5.0) (2019-01-10)
### Features
* ability to create non default constructible objects, by defining private default constructor and making `friend class bitsery::Access;` to access it.
It is not necessary to enforce class invariant immediately, because internal object representation will be overriden anyway.
### Improvements
* `StdSmartPtr` supports `std::unique_ptr` with custom deleter.
* `*InputBufferAdapter`(all) can also accept const buffer;
### Bug fixes
* fixed deserialization in `bitsery/ext/std_map{set}` when target container is not empty.
* added missing template parameters for specializations on `std` containers in multiple files in `bitsery/ext/*`.
# [4.4.0](https://github.com/fraillt/bitsery/compare/v4.3.0...v4.4.0) (2019-01-08)
### Features
* new extensions **CompactValue** and **CompactValueAsObject**, stores integral values in less space if possible. This is useful when you're working with mostly small values, that in rare cases can be large.
E.g. `int64_t money = 8000;` will only use 2 bytes, instead of 8. **CompactValueAsObject** allows to use `ext()` overload, without specifying size of underlying type and sets BUFFER_OVERFLOW error if value doesn't fit in underlying type during deserialization.
### Improvements
* improved **PolymorphicContext**, allows to extend already registered hierarchy in one translation unit, using different type other than `PolymorphicBaseClass` to avoid symbol collision between translation units or libraries.
`registerBasesList` was modified, so that it could accept user defined type (instead of `PolymorphicBaseClass`) that is used to declare hierarchy, by default it is `PolymorphicBaseClass`.
This introduced breaking change, for those who used this syntax (`registerBasesList<MySerializer, Shape>({})`) during registration.
It is encouraged to define helper type, that could be used for registering hierarchy for serialization and deserialization [example](examples/smart_pointers_with_polymorphism.cpp).
`This is only relevant then you want to use **PolymorphicContext** between different translation units or libraries`.
```cpp
//libA
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {};
}
}
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
...
ctx.registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{}).
//otherLib
struct MySquare: Shape {...}
//now it must define different type (exactly the same as PolymorphicBaseClass) to declare hierarchy
template<typename TBase>
struct MyHierarchy {
using Childs = PolymorphicClassesList<>;
};
template <>
struct MyHierarchy<Shape>: bitsery::ext::bitsery::ext::PolymorphicClassesList<MySquare> {};
...
//notice that we pass MyHierarchy as second argument
ctx.registerBasesList<MySerializer, MyHierarchy>(MyPolymorphicClassesForRegistering{}).
```
* **PolymorphicContext** also get optional method `registerSingleBaseBranch`, that allows manually register hierarchies, this might be more convenient when using you need to register in different translation units (or libraries), but it is error-prone.
# [4.3.0](https://github.com/fraillt/bitsery/compare/v4.2.1...v4.3.0) (2018-08-23)
### Features
* added runtime polymorphism support for pointer like types (raw and smart pointers).
In order to enable polymorphism new **PolymorphicContext** was created. It provides capability to register classes with serializer/deserializer.
* runtime polymorphism can be customized, by replacing **StandardRTTI** from <bitsery/ext/utils/rtti_utils.h> header.
* added smart pointers support for std::unique_ptr, std::shared_ptr and std::weak_ptr via **StdSmartPtr** extension.
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, on some compilers can improve deserialization performance up to ~40%.
### Improvements
* creatly improved interface for extending/implementing support for pointer like types. Now all pointer like types extends from **PointerObjectExtensionBase** and implements/configures required details.
* reimplemented **PointerOwner**, **PointerObserver**, **ReferencedByPointer**.
* reimplemented **PointerLinkingContext** to properly support shared objects and runtime polymorphism, pointer ownership for shared objects now has two states: SharedOwner e.g. std::shared_ptr and SharedObserver std::weak_ptr.
### Other notes
There is one *minor?* issue/limitation for pointer like types that uses virtual inheritance. When several pointers points to same object through different static type. it will not work correctly e.g.:
```cpp
struct Derived: virtual Base {...};
struct MyData {
std::shared_ptr<Derived> sptr;
std::weak_ptr<Base> wptddr;
}
```
In this example wptr and sptr have different static type, and *Derived* is virtually inherited from *Base*, so I get different pointer address for different types.
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)
### Improvements
@@ -106,7 +207,7 @@ Be careful when using deserializing untrusted data and make sure to enforce fund
### Features
* refactored interface, now works with C++11 compiler.
* new new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
* new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
* old consumer: correctly read old interfce and ignore new data.
* new consumer: get defaults (zero values) for new fields, when reading old data.
* added new extension for associative *map* containers **ContainerMap**.

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
project(bitsery
LANGUAGES CXX
VERSION 4.2.1)
VERSION 4.6.0)
#======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
@@ -41,13 +41,6 @@ install(DIRECTORY include/bitsery
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
#================ handle sub-projects =====================
# examples and tests are independend directories, that can be built separatelly.
# make find_package to no-op, when searching for "Bitsery", because it is created here.
macro(find_package)
if (NOT ${ARGV0} STREQUAL Bitsery)
_find_package(${ARGV})
endif()
endmacro()
if (BITSERY_BUILD_EXAMPLES)
message("build bitsery examples")

View File

@@ -26,6 +26,16 @@ you contribute:
6. Open a pull request against Bitsery *master* branch. Currently ongoing development is on *master*. At some point an integration branch will be set-up, and pull-requests should target that, but for now its all against master. You may see feature branches come and go, too.
If you're working with visual studio, there is how to build and run all tests from command line
```shell
mkdir build
cd build
cmake -DBITSERY_BUILD_TESTS=ON -DGTEST_ROOT="<PATH to GTEST>" -DCMAKE_CXX_FLAGS_RELEASE=/MT ..
cmake --build . --config Release
(cd tests && ctest -C Release && cd ..)
```
/MT option might be optional, depending on how gtest was built.
## Style guide
Just use your own judgment and stick to the style of the surrounding code.
Just use your own judgment and stick to the style of the surrounding code.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Mindaugas Vinkelis
Copyright (c) 2018 Mindaugas Vinkelis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -19,11 +19,11 @@ All cross-platform requirements are enforced at compile time, so serialized data
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
* forward/backward compatibility for your types.
* raw pointers (no polymorphism yet).
* smart and raw pointers with customizable runtime polymorphism support.
* fine-grained bit-level serialization control.
* Easily extendable for any type.
* Allows flexible or/and verbose syntax for better serialization control.
* Configurable endianess support.
* Configurable endianness support.
* No macros.
## Why to use bitsery
@@ -96,6 +96,8 @@ This documentation comprises these parts:
Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery.h>` and you're done.
> some **bitsery** extensions might require higher C++ standard (e.g. `StdVariant`)
## Platforms
This library was tested on
@@ -105,4 +107,4 @@ This library was tested on
## License
**bitsery** is licensed under the [MIT license](LICENSE).
**bitsery** is licensed under the [MIT license](LICENSE).

View File

@@ -11,34 +11,42 @@ Library design:
* `forward/backward compatibility via Growable extension`
* `pointers`
* `inheritance`
* `polymorphism`
Core Serializer/Deserializer functions (alphabetical order):
* `align`
* `boolValue`
* `container`
* `ext`
* `context`
* `context<T>`
* `contextOrNull<T>`
* `object`
* `text`
* `value`
* `align` (1.0.0)
* `boolValue` (4.0.0)
* `container` (1.0.0)
* `ext` (2.0.0)
* `context` (3.0.0)
* `context<T>` (4.1.0)
* `contextOrNull<T>` (4.2.0)
* `object` (1.0.0)
* `text` (1.0.0)
* `value` (1.0.0)
Serializer/Deserializer extensions via `ext` method (alphabetical order):
* `BaseClass`
* `Entropy`
* `Growable`
* `PointerOwner`
* `PointerObserver`
* `ReferencedByPointer`
* `StdMap`
* `StdOptional`
* `StdQueue`
* `StdSet`
* `StdStack`
* `ValueRange`
* `VirtualBaseClass`
* `BaseClass` (4.2.0)
* `CompactValue` (4.4.0)
* `CompactValueAsObject` (4.4.0)
* `Entropy` (3.0.0)
* `Growable` (3.0.0)
* `PointerOwner` (4.1.0)
* `PointerObserver` (4.1.0)
* `ReferencedByPointer` (4.1.0)
* `StdDuration` (4.6.0)
* `StdMap` (3.0.0)
* `StdOptional` (2.0.0)
* `StdQueue` (4.0.0)
* `StdSet` (4.0.0)
* `StdSmartPrt` (4.3.0)
* `StdStack` (4.0.0)
* `StdTimePoint` (4.6.0)
* `StdTuple` (4.6.0) (requires c++17)
* `StdVariant` (4.6.0) (requires c++17)
* `ValueRange` (3.0.0)
* `VirtualBaseClass` (4.2.0)
AdapterWriter/Reader functions:
* `writeBits/readBits`
@@ -65,7 +73,7 @@ Output adapters (buffer and stream) functions:
Tips and tricks:
* if you're getting static assert "please define 'serialize' function", most likely it is because your **serialize** function is not defined in same namespace as object.
* if you're getting static assert "please define 'serialize' function", please define **serialize** function in same namespace as object, or in **bitsery** namespace, for more info [ADL](https://en.cppreference.com/w/cpp/language/adl).
Other:
* [Contributing](../CONTRIBUTING.md)

View File

@@ -33,12 +33,12 @@ Now let's review features in more detail.
* **Cross-platform compatible.** if same code compiles on Android, PS3 console, and your PC either x64 or x86 architecture, you are 100% sure it works.
To achieve this, bitsery specifically defines size of underlying data, hence syntax is *value\<2\>* (alias function *value2b*) instead or *value*, or *container2b* for element type of 16bits, eg int16_t.
Bitsery also applies endianess transformation if nessesarry.
* **Flexible syntax.** if you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b* you can use flexible syntax.
Bitsery also applies endianness transformation if necessary.
* **Flexible syntax.** If you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b*, you can use flexible syntax.
Just include <bitsery/flexible.h> and can write like in [cereal](http://uscilab.github.io/cereal/).
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it accross multiple platforms.
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it across multiple platforms.
* **Optimized for speed and space.** library itself doesn't do any allocations (except if you use backward/forward compatibility) so data writing/reading is fast as memcpy to/from your buffer.
It also doesn't serialize any type information, all information needed is writen in your code!
It also doesn't serialize any type information, all information needed is written in your code!
* **No code generation required: no IDL or metadata** since it doesn't support any other formats except binary, it doesn't need any metadata.
* **Runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*, this way bitsery ensures that no malicious data crash you.
* **Supports forward/backward compatibility for your types** library has optional forward/backward compatibility for types implemented in *AdapterReader/Writer* by allowing to have inner data sessions inside buffer.
@@ -65,5 +65,5 @@ Bitsery allows to use bit-level operations and has two extensions that use them:
You want to support your custom container, its fine there is *ContainerTraits* for this, only few methods required to implement.
To use same container for buffer writing/reading add specialization to *BufferAdapterTraits*.
You want to customize serialization flow - use extensions, only two methods to define, and *ExtensionTraits* to further customize usage.
* **Configurable endianess support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
* **Configurable endianness support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
* **No macros.** Not so much to say, if you are like me, then it's a feature :)

View File

@@ -2,11 +2,11 @@
This is a quick guide to get **bitsery** up and running in a matter of minutes.
The only prerequisite for running bitsery is a modern C++11 compliant compiler, such as GCC 4.9.4, clang 3.4, MSVC 2015, or newer.
Older versions might work, but it is not tested.
Older versions might work, but they have not been tested.
## Get bitsery
bitsery can be directly included in your project or installed anywhere you can access header files.
**bitsery** can be directly included in your project or installed anywhere you can access header files.
Grab the latest version, and include directory `bitsery_base_dir/include/` to your project.
There's nothing to build or make - **bitsery** is header only.
@@ -27,11 +27,14 @@ using InputAdapter = InputBufferAdapter<Buffer>;
```
**bitsery** is very lightweight, so we need to explicitly include what we need.
* `<bitsery/bitsery.h>` is a core header, that includes our Serializer and Deserializer
* `<bitsery/adapter/buffer.h>` in order to write/read data we need specific adapter, depending on what underlying buffer will be. In this example we'll be using std::vector as our buffer, so we include buffer adapter.
* <bitsery/traits/...> traits tells library how efficiently serialize particular container.
create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
Include | Description
--|--
`<bitsery/bitsery.h>` | This is a core header, that includes our Serializer and Deserializer.
`<bitsery/adapter/buffer.h>` | In order to write/read data, we need a specific adapter, depending on what underlying buffer will be. In this example, we'll be using `std::vector` as our buffer, so we include the buffer adapter.
`<bitsery/traits/...>` | Traits tell the library how to efficiently serialize a particular container. Many common STL containers are supported out of the box.
Create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
## Add serialization method for your type
@@ -124,4 +127,4 @@ int main() {
}
```
**currently documentation and tutorial is progress, but for more usage examples see examples folder**
**currently documentation and tutorial is progress, but for more usage examples see examples folder**

View File

@@ -38,5 +38,8 @@ foreach(ExampleFile ${ExampleFiles})
get_filename_component(ExampleName ${ExampleFile} NAME_WE)
add_executable(bitsery.example.${ExampleName} ${ExampleFile})
target_link_libraries(bitsery.example.${ExampleName} PRIVATE Bitsery::bitsery)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(bitsery.example.${ExampleName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
endif()
endforeach()

View File

@@ -0,0 +1,115 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
// include extensions to work with tuples and variants
// these extesions only work with C++17
#if __cplusplus > 201402L
#include <bitsery/ext/std_tuple.h>
#include <bitsery/ext/std_variant.h>
// let's include this extension to make it more interesting :)
#include <bitsery/ext/compact_value.h>
struct MyStruct {
std::vector<int32_t> v{};
float f{};
bool operator==(const MyStruct& rhs) const {
return v == rhs.v && f == rhs.f;
}
};
template<typename S>
void serialize(S& s, MyStruct& o) {
s.container4b(o.v, 1000);
s.value4b(o.f);
}
// this will be the type that we want to serialize/deserialize
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{
// 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{
// 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>{},
// 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>{},
// 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)
// ext::OverloadExtObject<MyTuple, ext::StdTuple<ext::OverloadValue<float, 4>>>{},
// we can also override default 'serialize' function by creating an overloading for that type
[](S& s, MyStruct& o) {
s.value4b(o.f);
s.container(o.v, 1000, [&s](int32_t& v) {
s.ext4b(v, ext::CompactValue{});
});
},
// NOTE.
// it is possible to provide "auto" as type parameter
// this will allow you to override all default 'serialize' functions
// but in this case it will not be called, because we have explicitly provided overloads for all variant types
// also note, that first parameter (serializer) is also "auto", this is required, so that it would be least specialized case
// otherwise it will not compile if you any ext::Overload* helper defined, because it will have ambiguous definitions
// (ext::OverLoad* defines (templated_type& s, concrete_type& o) and lambda would be (concrete_type& s, templated_type& o))
[](auto& , auto& ) {
assert(false);
}
});
}
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyVariant data{MyTuple{-7549, {{-451, 2, 968, 75, 4, 156, 49}, 874.4f}}};
// MyVariant data{MyStruct{{-451, 2, 968, 75, 4, 156, 49}, 874.4f}};
MyVariant res{};
//create buffer to store data
Buffer buffer;
//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);
//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);
assert(state.first == ReaderError::NoError && state.second);
assert(data == res);
}
#else
#if defined(_MSC_VER)
#pragma message("example works only on c++17")
#else
#warning "example works only on c++17"
#endif
int main() {
return 0;
}
#endif

View File

@@ -70,6 +70,10 @@ using InputAdapter = InputBufferAdapter<Buffer>;
// s.template context<MyContext>();
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
//NOTE:
// if your context has no additional usage outside of serialization flow,
// then you can create it internally via configuration (see inheritance.cpp)
int main() {
MyTypes::GameState data{};

View File

@@ -1,5 +1,5 @@
//
//this example coverls all the corner cases that can happen using inherintace
//this example covers all the corner cases that can happen using inheritance
//in reality virtual inherintance is usually avoided, so your code would look much simpler.
//
@@ -11,7 +11,7 @@
//this header contains two extensions, that specifies inheritance type of base class
// BaseClass - normal inheritance
// VirtualBaseClass - when virtual inheritance is used
//in order for virtual inheritance to work, InheritanceContext is required.
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
//it can be created either internally (via configuration) or externally (pointer to context).
#include <bitsery/ext/inheritance.h>

View File

@@ -0,0 +1,65 @@
//
// example of how to deserialize non default constructible objects
//
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
class MyData {
//define your private data
float _x{0};
float _y{0};
//make bitsery:Access friend
friend class bitsery::Access;
//create default constructor, don't worry about class invariant, it will be restored in deserialization
MyData() = default;
//define serialize function
template <typename S>
void serialize(S& s) {
s.value4b(_x);
s.value4b(_y);
}
public:
//define non default public constructor
MyData(float x, float y):_x{x}, _y{y} {}
//this is for convenience
bool operator ==(const MyData&rhs) const {
return _x == rhs._x && _y == rhs._y;
}
};
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
int main() {
//initialize our data
std::vector<MyData> data{};
data.emplace_back(145.4f, 84.48f);
std::vector<MyData> res{};
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
//create buffer
Buffer buffer{};
//create and serialize container, and get written bytes count
Serializer<OutputAdapter> ser{OutputAdapter{buffer}};
ser.container(data, 10);
auto writtenBytes = AdapterAccess::getWriter(ser).writtenBytesCount();
//create and deserialize container
Deserializer<InputAdapter> des{InputAdapter{buffer.begin(), writtenBytes}};
des.container(res, 10);
//check if everything went ok
auto& reader = AdapterAccess::getReader(des);
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
assert(res == data);
}

View File

@@ -0,0 +1,273 @@
//
// Created by fraillt on 18.4.26.
//
#include <cassert>
#include <memory>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/std_smart_ptr.h>
//in order to work with polymorphic types, we need to describe few steps:
// 1) describe relationships between base and derived types
// this will allow to know what are possible types reachable from base class
// 2) bind serializer to base class
// this will allow to iterate through all types, and add serialization functions,
// without this step compiler would simply remove functions that are not bound at compile-time even it we use type at runtime.
using bitsery::ext::BaseClass;
using bitsery::ext::PointerObserver;
using bitsery::ext::StdSmartPtr;
//define our data structures
struct Color {
float r{}, g{}, b{};
bool operator == (const Color& o) const {
return std::tie(r, g, b) ==
std::tie(o.r, o.g, o.b);
}
};
struct Shape {
Color clr{};
virtual ~Shape() = 0;
};
Shape::~Shape() = default;
struct Circle : Shape {
int32_t radius{};
bool operator == (const Circle& o) const {
return std::tie(radius, clr) ==
std::tie(o.radius, o.clr);
}
};
struct Rectangle : Shape {
int32_t width{};
int32_t height{};
bool operator == (const Rectangle& o) const {
return std::tie(width, height, clr) ==
std::tie(o.width, o.height, o.clr);
}
};
struct RoundedRectangle : Rectangle {
int32_t radius{};
bool operator == (const RoundedRectangle& o) const {
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
std::tie(o.radius, static_cast<const Rectangle&>(o));
}
};
//define serialization functions
template<typename S>
void serialize(S &s, Color &o) {
//in real world scenario, it might be possible to serialize this using ValueRange, to map values in smaller space
//but for the sake of this example keep it simple
s.value4b(o.r);
s.value4b(o.g);
s.value4b(o.b);
}
template<typename S>
void serialize(S &s, Shape &o) {
s.object(o.clr);
}
template<typename S>
void serialize(S &s, Circle &o) {
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.radius);
}
template<typename S>
void serialize(S &s, Rectangle &o) {
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.width);
s.value4b(o.height);
}
template<typename S>
void serialize(S &s, RoundedRectangle &o) {
s.ext(o, bitsery::ext::BaseClass<Rectangle>{});
s.value4b(o.radius);
}
//define our test structure
struct SomeShapes {
std::vector<std::shared_ptr<Shape>> sharedList;
std::unique_ptr<Shape> uniquePtr;
//weak ptr and refPtr will point to sharedList
std::weak_ptr<Shape> weakPtr;
Shape* refPtr;
};
//creates object, and populates some data
SomeShapes createData() {
SomeShapes data{};
{
auto tmp = new RoundedRectangle{};
tmp->height = 151572;
tmp->width = 488795;
tmp->radius = 898;
tmp->clr.r = 0.5f;
tmp->clr.g = 1.0f;
tmp->clr.b = 1.0f;
data.uniquePtr.reset(tmp);
}
{
auto tmp = new Circle{};
tmp->radius = 75987;
tmp->clr.r = 0.5f;
tmp->clr.g = 0.0f;
tmp->clr.b = 1.0f;
data.sharedList.emplace_back(tmp);
}
{
auto tmp = new Rectangle{};
tmp->height = 15157;
tmp->width = 48879;
tmp->clr.r = 1.0f;
tmp->clr.g = 0.0f;
tmp->clr.b = 0.0f;
data.sharedList.emplace_back(tmp);
}
data.weakPtr = data.sharedList[0];
data.refPtr = data.sharedList[1].get();
return data;
}
template<typename S>
void serialize(S &s, SomeShapes &o) {
s.ext(o.uniquePtr, StdSmartPtr{});
// to make things more interesting first serialize weakPtr and refPtr,
// even though objects that weakPtr and refPtr is serialized later,
// bitsery will work regardless
s.ext(o.weakPtr, StdSmartPtr{});
s.ext(o.refPtr, PointerObserver{});
s.container(o.sharedList, 100, [&s](std::shared_ptr<Shape> &item) {
s.ext(item, StdSmartPtr{});
});
}
// STEP 1
// define relationships between base and derived classes
namespace bitsery {
namespace ext {
//for each base class define DIRECTLY derived classes
//e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle, RoundedRectangle>
// is incorrect, because RoundedRectangle does not directly derive from Shape
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
};
template<>
struct PolymorphicBaseClass<Rectangle> : PolymorphicDerivedClasses<RoundedRectangle> {
};
}
}
// convenient type that stores all our types, so that we could easily register and
// 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 OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = 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>>;
//NOTE:
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
using MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, TContext>;
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, TContext>;
//checks if deserialized data is equal
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
{
auto d = dynamic_cast<RoundedRectangle *>(data.uniquePtr.get());
auto r = dynamic_cast<RoundedRectangle *>(res.uniquePtr.get());
assert(r != nullptr);
assert(*d == *r);
}
{
auto d = dynamic_cast<Circle *>(data.sharedList[0].get());
auto r = dynamic_cast<Circle *>(res.sharedList[0].get());
assert(r != nullptr);
assert(*d == *r);
}
{
auto d = dynamic_cast<Rectangle *>(data.sharedList[1].get());
auto r = dynamic_cast<Rectangle *>(res.sharedList[1].get());
assert(r != nullptr);
assert(*d == *r);
}
assert(res.weakPtr.lock().get() == res.sharedList[0].get());
assert(res.refPtr == res.sharedList[1].get());
}
int main() {
auto data = createData();
//create buffer to store data
Buffer buffer{};
size_t writtenSize{};
{
//STEP 2
//bind serializer with base polymorphic types, it will go through all reachable classes that is defined in first step.
//so you dont need to add Rectangle to reach for RoundedRectangle
TContext ctx{};
std::get<1>(ctx).registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{});
//serialize our data
MySerializer ser{OutputAdapter{buffer}, &ctx};
ser.object(data);
auto &w = AdapterAccess::getWriter(ser);
w.flush();
writtenSize = w.writtenBytesCount();
//make sure that pointer linking context is valid
//this ensures that all non-owning pointers points to data that has been serialized,
//so we can successfully reconstruct pointers after deserialization
assert(std::get<0>(ctx).isValid());
}
SomeShapes res{};
{
TContext ctx{};
std::get<1>(ctx).registerBasesList<MyDeserializer>(MyPolymorphicClassesForRegistering{});
//serialize our data
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
des.object(res);
auto &r = AdapterAccess::getReader(des);
//check if everything went find
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
//also check for dangling pointers, after deserialization
assert(std::get<0>(ctx).isValid());
// clear shared state from pointer linking context,
// it is only required if there are any pointers that manage shared state, e.g. std::shared_ptr
assert(res.weakPtr.use_count() == 2);//one in sharedList and one in pointer linking context
std::get<0>(ctx).clearSharedState();
assert(res.weakPtr.use_count() == 1);
}
assertSameShapes(data, res);
return 0;
}

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_BUFFER_H
#define BITSERY_ADAPTER_BUFFER_H
@@ -30,15 +29,21 @@
namespace bitsery {
//base class that stores container iterators, and is required for session support (for reading sessions data)
template <typename Buffer>
template<typename Buffer>
class BufferIterators {
protected:
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
using BuffNonConst = typename std::remove_const<Buffer>::type;
protected:
using TIterator = typename std::conditional<isConstBuffer,
typename traits::BufferAdapterTraits<BuffNonConst>::TConstIterator,
typename traits::BufferAdapterTraits<BuffNonConst>::TIterator>::type;
static_assert(details::IsDefined<TIterator>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
BufferIterators(TIterator begin, TIterator end)
:posIt{begin},
endIt{end}
{
: posIt{begin},
endIt{end} {
}
friend details::SessionAccess;
@@ -47,26 +52,25 @@ namespace bitsery {
TIterator endIt;
};
template <typename Buffer>
class InputBufferAdapter: public BufferIterators<Buffer> {
template<typename Buffer>
class InputBufferAdapter : public BufferIterators<Buffer> {
public:
using TIterator = typename BufferIterators<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BufferAdapter only works with contiguous containers");
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
static_assert(details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
"BufferAdapter only works with contiguous containers");
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
{
InputBufferAdapter(TIterator begin, TIterator endIt)
: BufferIterators<Buffer>(begin, endIt) {
}
InputBufferAdapter(TIterator begin, size_t size)
:InputBufferAdapter(begin, std::next(begin, size))
{
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
}
void read(TValue* data, size_t size) {
void read(TValue *data, size_t size) {
//for optimization
auto tmp = this->posIt;
this->posIt += size;
@@ -76,7 +80,6 @@ namespace bitsery {
this->posIt -= size;
//set everything to zeros
std::memset(data, 0, size);
if (error() == ReaderError::NoError)
setError(ReaderError::DataOverflow);
}
@@ -102,6 +105,47 @@ namespace bitsery {
}
};
template<typename Buffer>
class UnsafeInputBufferAdapter : public BufferIterators<Buffer> {
public:
using TIterator = typename BufferIterators<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
static_assert(details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
"BufferAdapter only works with contiguous containers");
UnsafeInputBufferAdapter(TIterator beginIt, TIterator endIt) : BufferIterators<Buffer>(beginIt, endIt) {
}
UnsafeInputBufferAdapter(TIterator begin, size_t size)
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
}
void read(TValue *data, size_t size) {
//for optimization
auto tmp = this->posIt;
this->posIt += size;
assert(std::distance(this->posIt, this->endIt) >= 0);
std::memcpy(data, std::addressof(*tmp), size);
}
ReaderError error() const {
return err;
}
void setError(ReaderError error) {
err = error;
}
bool isCompletedSuccessfully() const {
return this->posIt == this->endIt;
}
private:
ReaderError err = ReaderError::NoError;
};
template<typename Buffer>
class OutputBufferAdapter {
@@ -110,17 +154,17 @@ namespace bitsery {
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BufferAdapter only works with contiguous containers");
static_assert(details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<Buffer>::isContiguous,
"BufferAdapter only works with contiguous containers");
OutputBufferAdapter(Buffer &buffer)
: _buffer{std::addressof(buffer)}
{
: _buffer{std::addressof(buffer)} {
init(TResizable{});
}
void write(const TValue *data, size_t size) {
writeInternal(data, size, TResizable{});
}
@@ -136,7 +180,7 @@ namespace bitsery {
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
Buffer* _buffer;
Buffer *_buffer;
TIterator _outIt{};
TIterator _end{};
@@ -163,7 +207,7 @@ namespace bitsery {
#else
auto tmp = _outIt;
_outIt += size;
if (std::distance(_outIt , _end) >= 0) {
if (std::distance(_outIt, _end) >= 0) {
std::memcpy(std::addressof(*tmp), data, size);
#endif
} else {
@@ -201,7 +245,6 @@ namespace bitsery {
}
};
}
#endif //BITSERY_ADAPTER_BUFFER_H

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_STREAM_H
#define BITSERY_ADAPTER_STREAM_H
@@ -28,7 +27,6 @@
#include "../traits/array.h"
#include <ios>
namespace bitsery {
template <typename TChar, typename CharTraits>

View File

@@ -20,8 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_READER_H
#define BITSERY_ADAPTER_READER_H
@@ -29,7 +27,6 @@
#include <algorithm>
#include <cstring>
namespace bitsery {
template <typename TReader>
@@ -63,7 +60,6 @@ namespace bitsery {
~AdapterReader() noexcept = default;
template<size_t SIZE, typename T>
void readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
@@ -115,24 +111,20 @@ namespace bitsery {
}
}
const InputAdapter& adapter() const {
return _inputAdapter;
}
private:
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
InputAdapter _inputAdapter;
typename std::conditional<Config::BufferSessionsEnabled,
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
_session;
template<typename T>
void directRead(T *v, size_t count) {
static_assert(!std::is_const<T>::value, "");
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
//swap each byte if nessesarry
//swap each byte if necessary
_swapDataBits(v, count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
}
@@ -201,7 +193,6 @@ namespace bitsery {
}
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
@@ -249,7 +240,7 @@ namespace bitsery {
auto bitsLeft = size;
T res{};
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, details::BitsSize<UnsignedValue>::value);
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
UnsignedValue tmp;
_reader.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);

View File

@@ -20,8 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_WRITER_H
#define BITSERY_ADAPTER_WRITER_H
@@ -30,7 +28,6 @@
#include <cassert>
#include <utility>
namespace bitsery {
template <typename Config>
@@ -100,7 +97,6 @@ namespace bitsery {
//helper type for default config
using MeasureSize = BasicMeasureSize<DefaultConfig>;
template <typename TWriter>
class AdapterWriterBitPackingWrapper;
@@ -174,10 +170,6 @@ namespace bitsery {
_session.end(*this);
}
const OutputAdapter& adapter() const {
return _outputAdapter;
}
private:
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
template<typename T>
@@ -303,7 +295,7 @@ namespace bitsery {
auto value = v;
auto bitsLeft = size;
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, valueSize);
auto bits = (std::min)(bitsLeft, valueSize);
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
_scratchBits += bits;
if (_scratchBits >= valueSize) {
@@ -332,7 +324,7 @@ namespace bitsery {
}
}
const UnsignedType _MASK = std::numeric_limits<UnsignedType>::max();
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
ScratchType _scratch{};
size_t _scratchBits{};
TWriter& _writer;

View File

@@ -25,8 +25,8 @@
#define BITSERY_BITSERY_H
#define BITSERY_MAJOR_VERSION 4
#define BITSERY_MINOR_VERSION 2
#define BITSERY_PATCH_VERSION 1
#define BITSERY_MINOR_VERSION 6
#define BITSERY_PATCH_VERSION 0
#define BITSERY_QUOTE_MACRO(name) #name
#define BITSERY_BUILD_VERSION_STR(major,minor, patch) \

View File

@@ -29,7 +29,7 @@
namespace bitsery {
/*
* endianess
* endianness
*/
enum class EndiannessType {
LittleEndian,

View File

@@ -29,6 +29,7 @@
#include <vector>
#include <stack>
#include <cstring>
#include <climits>
#include "adapter_utils.h"
#include "not_defined_type.h"
@@ -40,7 +41,7 @@ namespace bitsery {
template<typename T>
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
};
//add swap functions to class, to avoid compilation warning about unused functions

View File

@@ -35,7 +35,7 @@ namespace bitsery {
NotDefinedType(T&& ...){}
NotDefinedType() = default;
//define operators so that we also swallow deeper errors, to reduce error stack
//this time will be used as iterator, so define all operators nessesarry to work with iterators
//this time will be used as iterator, so define all operators necessary to work with iterators
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
return true;
}
@@ -56,7 +56,7 @@ namespace bitsery {
int& operator*() {
return data;
}
int data;
int data{};
};
template <typename T>

View File

@@ -32,17 +32,29 @@
namespace bitsery {
//this allows to call private serialize method for the class
//just make friend it to that class
struct Access {
//this allows to call private serialize method, and construct instance (if no default constructor is provided) for your type
//just make friend it in your class
class Access {
public:
template<typename S, typename T>
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
obj.serialize(s);
}
template <typename T>
static T create() {
//if you get an error here, please create default constructor
return T{};
}
template <typename T>
static T* createInHeap() {
return new T{};
}
};
//when call to serialize function is ambiguous (member and non-member serialize function exists for a type)
//specialize this class, by inheriting from either UseNonMemberFnc or UseMemberFnc
//specialize this class by inheriting from either UseNonMemberFnc or UseMemberFnc
//e.g.
//template <> struct SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
template<typename T>
@@ -56,7 +68,7 @@ namespace bitsery {
};
//serializer/deserializer, does not public interface to get underlying writer/reader
//serializer/deserializer, does not have public interface to get underlying writer/reader
//to prevent users from using writer/reader directly, because they have different interface
//and they cannot be used describing serialization flows.: use extensions for this reason.
//this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions.
@@ -173,12 +185,12 @@ namespace bitsery {
#endif
//used for extensions, when extension TValue = void
//used for extensions when extension TValue = void
struct DummyType {
};
/*
* this includes all integral types floats and enums(except bool)
* this includes all integral types, floats and enums(except bool)
*/
template<typename T>
struct IsFundamentalType : std::integral_constant<bool,
@@ -230,6 +242,10 @@ namespace bitsery {
selectSerializeFnc(s, v, SelectSerializeFnc<TDecayed>{});
}
static constexpr bool isDefined() {
return HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value;
}
private:
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 0>) {
static_assert(!(HasSerializeFunction<S, T>::value && HasSerializeMethod<S, T>::value),
@@ -312,13 +328,13 @@ namespace bitsery {
template<typename TCast, typename ... Args>
TCast *getContextImpl(std::tuple<Args...> *ctx, std::true_type) {
using TCastIndex = GetTypeIndex<TCast, Args...>;
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Type doesn't exists.");
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
return std::addressof(std::get<TCastIndex::value>(*ctx));
}
template<typename TCast, typename TContext>
TCast *getContextImpl(TContext *ctx, std::false_type) {
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Type doesn't exists.");
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
return static_cast<TCast *>(ctx);
}

View File

@@ -1,6 +1,24 @@
//MIT License
//
// Created by fraillt on 17.10.5.
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_SESSIONS_H
#define BITSERY_DETAILS_SESSIONS_H

View File

@@ -0,0 +1,185 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_COMPACT_VALUE_H
#define BITSERY_EXT_COMPACT_VALUE_H
#include "../details/serialization_common.h"
#include "../details/adapter_common.h"
#include <cassert>
namespace bitsery {
namespace details {
template <bool CheckOverflow>
class CompactValueImpl {
public:
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &s, Writer &writer, const T &v, Fnc &&) const {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
serializeImpl(s, writer, reinterpret_cast<const TValue&>(v), std::integral_constant<bool, sizeof(T) != 1>{});
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &d, Reader &reader, T &v, Fnc &&) const {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
deserializeImpl(d, reader, reinterpret_cast<TValue &>(v), std::integral_constant<bool, sizeof(T) != 1>{});
}
private:
// if value is 1byte size, just serialize/ deserialize whole value
template<typename Ser, typename Writer, typename T>
void serializeImpl(Ser &s, Writer &, const T &v, std::false_type) const {
s.value1b(v);
}
template<typename Des, typename Reader, typename T>
void deserializeImpl(Des &d, Reader &, T &v, std::false_type) const {
d.value1b(v);
}
// when value is bigger than 1byte size,
template<typename Ser, typename Writer, typename T>
void serializeImpl(Ser &, Writer &writer, const T &v, std::true_type) const {
auto val = zigZagEncode(v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
writeBytes(writer, val);
}
template<typename Des, typename Reader, typename T>
void deserializeImpl(Des &, Reader &reader, T &v, std::true_type) const {
using TUnsigned = SameSizeUnsigned<T>;
TUnsigned res{};
readBytes(reader, res);
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
}
// zigzag encode signed types
template<typename T>
const SameSizeUnsigned<T> &zigZagEncode(const T &v, std::false_type) const {
return v;
}
template<typename TResult, typename TUnsigned>
const TResult &zigZagDecode(const TUnsigned &v, std::false_type) const{
return v;
}
template<typename T>
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
return (v << 1) ^ (v >> (BitsSize<T>::value - 1));
}
template<typename TResult, typename TUnsigned>
TResult zigZagDecode(TUnsigned v, std::true_type) const {
return (v >> 1) ^ (~(v & 1) + 1); // same as -(v & 1), but no warning on VisualStudio
}
// write/read bytes one by one
template<typename Writer, typename T>
void writeBytes(Writer &w, const T &v) const {
auto val = v;
while(val > 0x7Fu) {
w.template writeBytes<1>(static_cast<uint8_t>(val | 0x80u));
val >>=7u;
}
w.template writeBytes<1>(static_cast<uint8_t>(val));
}
template<typename Reader, typename T>
void readBytes(Reader &r, T &v) const {
constexpr auto TBITS = sizeof(T)*8;
uint8_t b1{0x80u};
auto i = 0u;
for (;i < TBITS && b1 > 0x7Fu; i +=7u) {
r.template readBytes<1>(b1);
v += static_cast<T>(b1 & 0x7Fu) << i;
}
checkReadOverflow<Reader, T>(r, i, b1, std::integral_constant<bool, CheckOverflow>{});
}
template <typename Reader, typename T>
void checkReadOverflow(Reader &r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
constexpr auto TBITS = sizeof(T)*8;
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
r.setError(bitsery::ReaderError::DataOverflow);
}
}
template <typename Reader, typename T>
void checkReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
}
};
}
namespace ext {
// this type will use value overload, and do not check if type is sufficiently large during deserialization
class CompactValue: public details::CompactValueImpl<false> {};
// this type will enable object overload, and set DataOverflow if value doesn't fit in type, during deserialization
class CompactValueAsObject: public details::CompactValueImpl<true> {};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::CompactValue, T> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
// disable object overload, because we don't have implemented serialization function for fundamental types
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::CompactValueAsObject, T> {
// use dummy implemenations for value and object overload
using TValue = void;
// only enable object overload
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T, bool Check>
struct ExtensionTraits<details::CompactValueImpl<Check>, T> {
using TValue = T;
static constexpr bool SupportValueOverload = !Check;
static constexpr bool SupportObjectOverload = Check;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_COMPACT_VALUE_H

View File

@@ -23,150 +23,161 @@
#ifndef BITSERY_EXT_POINTER_H
#define BITSERY_EXT_POINTER_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
namespace bitsery {
namespace ext {
namespace details_pointer {
namespace pointer_details {
template<typename S>
PointerLinkingContext &getLinkingContext(S &s) {
auto res = s.template context<PointerLinkingContext>();
assert(res != nullptr);
return *res;
}
template<typename T>
struct PtrOwnerManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
template<typename TObject>
struct RawPointerObjectHandler {
using TPointer = TObject;
template<typename T>
void create(TObject &obj) const {
obj = new T{};
static TElement* getPtr(T &obj) {
return obj;
}
void destroy(TObject &obj) const {
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Owner;
}
static void assign(T& obj, TElement* valuePtr) {
delete obj;
obj = valuePtr;
}
static void clear(T& obj) {
delete obj;
obj = nullptr;
}
};
const TPointer getPtr(const TObject &obj) const {
template<typename T>
struct PtrObserverManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
//observer must return reference to pointer, so that it could be updated later
static TElement*& getPtrRef(T& obj) {
return obj;
}
TPointer getPtr(TObject &obj) const {
static TElement* getPtr(T& obj) {
return obj;
}
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Observer;
}
static void assign(T& obj, TElement* valuePtr) {
//do not delete existing object
obj = valuePtr;
}
static void clear(T& obj) {
obj = nullptr;
}
};
template <typename TObject>
struct RawPointerManagerConfig {
using RTTI = bitsery::ext::utils::StandardRTTI;
static constexpr PointerOwnershipType OwnershipType = PointerOwnershipType::Owner;
template<typename T>
struct NonPtrManager {
using Handler = RawPointerObjectHandler<TObject>;
static_assert(!std::is_pointer<T>::value, "");
static std::unique_ptr<utils::PointerSharedContextBase> createSharedContext(TObject &) {
return {};
using TElement = T;
static TElement* getPtr(T& obj) {
return &obj;
}
static void restoreFromSharedContext(TObject &, utils::PointerSharedContextBase *) {
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Owner;
}
// this code is unreachable for reference type, but is necessary to compile
// LCOV_EXCL_START
static void assign(T& , TElement* ) {}
static void clear(T& ) {}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI {
template<typename TBase>
static size_t get(TBase& ) {
return 0;
}
template<typename TBase>
static constexpr size_t get() {
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return false;
}
};
}
class PointerOwner : public utils::PointerOwnerManager<details_pointer::RawPointerManagerConfig> {
template<typename RTTI>
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
public:
explicit PointerOwner(PointerType ptrType = PointerType::Nullable) : PointerOwnerManager(ptrType) {}
};
class PointerObserver {
public:
explicit PointerObserver(PointerType ptrType = PointerType::Nullable) : _ptrType{ptrType} {}
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&) const {
auto &ctx = details_pointer::getLinkingContext(ser);
if (obj) {
details::writeSize(w, ctx.getInfoByPtr(obj, PointerOwnershipType::Observer).id);
} else {
assert(_ptrType == PointerType::Nullable);
details::writeSize(w, 0);
}
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &des, Reader &r, T &obj, Fnc &&) const {
size_t id{};
details::readSize(r, id, std::numeric_limits<size_t>::max());
if (id) {
auto &ctx = details_pointer::getLinkingContext(des);
ctx.getInfoById(id, PointerOwnershipType::Observer).processObserver(reinterpret_cast<void *&>(obj));
} else {
if (_ptrType == PointerType::Nullable)
obj = nullptr;
else
r.setError(ReaderError::InvalidPointer);
}
}
private:
PointerType _ptrType;
};
class ReferencedByPointer {
public:
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
auto &ctx = details_pointer::getLinkingContext(ser);
details::writeSize(w, ctx.getInfoByPtr(&obj, PointerOwnershipType::Owner).id);
fnc(const_cast<T &>(obj));
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
size_t id{};
details::readSize(r, id, std::numeric_limits<size_t>::max());
if (id) {
auto &ctx = details_pointer::getLinkingContext(des);
fnc(obj);
ctx.getInfoById(id, PointerOwnershipType::Owner).processOwner(&obj);
} else {
//cannot be null for references
r.setError(ReaderError::InvalidPointer);
}
}
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::PointerOwner, T *> {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//pointers cannot have lamba overload, when polymorphism support will be added
static constexpr bool SupportLambdaOverload = false;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T *> {
struct ExtensionTraits<ext::PointerObserver, T*> {
//although pointer observer doesn't serialize anything, but we still add value overload support to be consistent with pointer owners
//observer only writes/reads pointer id from pointer linking context
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//pointers cannot have lamba overload, when polymorphism support will be added
static constexpr bool SupportLambdaOverload = false;
};
@@ -182,5 +193,4 @@ namespace bitsery {
}
#endif //BITSERY_EXT_POINTER_H

View File

@@ -0,0 +1,94 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_CHRONO_H
#define BITSERY_EXT_STD_CHRONO_H
#include "../traits/core/traits.h"
#include <chrono>
namespace bitsery {
namespace ext {
class StdDuration {
public:
template<typename Ser, typename Writer, typename T, typename Period, typename Fnc>
void serialize(Ser&, Writer&, const std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
auto res = obj.count();
fnc(res);
}
template<typename Des, typename Reader, typename T, typename Period, typename Fnc>
void deserialize(Des&, Reader&, std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
T res{};
fnc(res);
obj = std::chrono::duration<T, Period>{res};
}
};
class StdTimePoint {
public:
template<typename Ser, typename Writer, typename Clock, typename T, typename Period, typename Fnc>
void serialize(Ser&, Writer&, const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
auto res = obj.time_since_epoch().count();
fnc(res);
}
template<typename Des, typename Reader, typename Clock, typename T, typename Period, typename Fnc>
void deserialize(Des&, Reader&, std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
T res{};
fnc(res);
auto dur = std::chrono::duration<T, Period>{res};
obj = std::chrono::time_point<Clock, std::chrono::duration<T, Period>>{dur};
}
};
}
namespace traits {
template<typename Rep, typename Period>
struct ExtensionTraits<ext::StdDuration, std::chrono::duration<Rep, Period>> {
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
template<typename Clock, typename Rep, typename Period>
struct ExtensionTraits<ext::StdTimePoint,
std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>> {
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_CHRONO_H

View File

@@ -25,6 +25,9 @@
#include "../traits/core/traits.h"
#include "../details/adapter_utils.h"
#include "../details/serialization_common.h"
//we need this, so we could reserve for non ordered map
#include <unordered_map>
namespace bitsery {
namespace ext {
@@ -53,17 +56,30 @@ namespace bitsery {
size_t size{};
details::readSize(reader, size, _maxSize);
auto hint = obj.begin();
obj.clear();
reserve(obj, size);
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
TKey key;
TValue value;
auto key{bitsery::Access::create<TKey>()};
auto value{bitsery::Access::create<TValue>()};
fnc(key, value);
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
}
}
private:
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename T>
void reserve(T& , size_t ) const {
//for ordered container do nothing
}
size_t _maxSize;
};
}

View File

@@ -24,23 +24,13 @@
#ifndef BITSERY_EXT_STD_OPTIONAL_H
#define BITSERY_EXT_STD_OPTIONAL_H
//this module do not include optional, but expects it to be declared in std::optional
//if you're using experimental optional from <experimental/optional>
//add it in std namespace like this:
//namespace std {
// template <typename T>
// using optional = experimental::optional<T>;
//}
#include <type_traits>
#include "../traits/core/traits.h"
#include "../details/serialization_common.h"
#include <optional>
namespace bitsery {
namespace ext {
template<typename T>
using std_optional = ::std::optional<T>;
class StdOptional {
public:
@@ -49,38 +39,27 @@ namespace bitsery {
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing/reading bool state of optional
*/
explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {}
template<typename T>
constexpr void assertType() const {
using TOpt = typename std::remove_cv<T>::type;
using TVal = typename TOpt::value_type;
static_assert(std::is_same<TOpt, std_optional<TVal>>(), "");
static_assert(std::is_default_constructible<TVal>::value, "");
};
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
assertType<T>();
void serialize(Ser &ser, Writer &, const std::optional<T> &obj, Fnc &&fnc) const {
ser.boolValue(static_cast<bool>(obj));
if (_alignBeforeData)
ser.align();
if (obj)
fnc(const_cast<typename T::value_type & >(*obj));
fnc(const_cast<T&>(*obj));
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
assertType<T>();
void deserialize(Des &des, Reader &, std::optional<T> &obj, Fnc &&fnc) const {
bool exists{};
des.boolValue(exists);
if (_alignBeforeData)
des.align();
if (exists) {
typename T::value_type tmp{};
fnc(tmp);
obj = tmp;
obj = ::bitsery::Access::create<T>();
fnc(*obj);
} else {
//experimental optional doesnt have .reset method
obj = T{};
obj = std::nullopt;
}
}
private:
@@ -90,8 +69,8 @@ namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdOptional, T> {
using TValue = typename T::value_type;
struct ExtensionTraits<ext::StdOptional, std::optional<T>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;

View File

@@ -51,15 +51,15 @@ namespace bitsery {
}
};
//inherit from queue so we could take underlying container
template <typename T, typename C>
struct PriorityQueueCnt : public std::priority_queue<T, C>
template <typename T, typename Seq, typename Cmp>
struct PriorityQueueCnt : public std::priority_queue<T, Seq, Cmp>
{
static const C& getContainer(const std::priority_queue<T, C>& s )
static const Seq& getContainer(const std::priority_queue<T, Seq, Cmp>& s )
{
//get address of underlying container
return s.*(&PriorityQueueCnt::c);
}
static C& getContainer(std::priority_queue<T, C>& s )
static Seq& getContainer(std::priority_queue<T, Seq, Cmp>& s )
{
//get address of underlying container
return s.*(&PriorityQueueCnt::c);
@@ -82,14 +82,14 @@ namespace bitsery {
}
//for priority_queue
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C> &obj, Fnc &&fnc) const {
ser.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
template<typename Ser, typename Writer, typename T, typename C, typename Comp, typename Fnc>
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
ser.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
void deserialize(Des &des, Reader &, std::priority_queue<T,C> &obj, Fnc &&fnc) const {
des.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
template<typename Des, typename Reader, typename T, typename C, typename Comp, typename Fnc>
void deserialize(Des &des, Reader &, std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
des.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
};

View File

@@ -25,9 +25,9 @@
#include <cassert>
#include "../details/adapter_utils.h"
//we need this, so we could
#include "../details/serialization_common.h"
//we need this, so we could reserve for non ordered set
#include <unordered_set>
#include "../traits/core/traits.h"
namespace bitsery {
namespace ext {
@@ -54,26 +54,28 @@ namespace bitsery {
size_t size{};
details::readSize(reader, size, _maxSize);
auto hint = obj.begin();
obj.clear();
reserve(obj, size);
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
TKey key;
auto key{bitsery::Access::create<TKey>()};
fnc(key);
hint = obj.emplace_hint(hint, std::move(key));
}
}
private:
template <typename T>
void reserve(std::unordered_set<T>& obj, size_t size) const {
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename T>
void reserve(std::unordered_multiset<T>& obj, size_t size) const {
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename T>
void reserve(T& , size_t ) const {
//for ordered container do nothing

View File

@@ -0,0 +1,129 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_SMART_PTR_H
#define BITSERY_EXT_STD_SMART_PTR_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
#include <memory>
namespace bitsery {
namespace ext {
namespace smart_ptr_details {
//further code is for managing shared ownership
//do not nest this type in pointer manager class itself, because it will be different type for different T
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase {
std::shared_ptr<void> obj{};
};
template<typename T>
struct SmartPtrOwnerManager {
using TElement = typename T::element_type;
template <typename TDeleter>
static TElement *getPtr(std::unique_ptr<TElement, TDeleter> &obj) {
return obj.get();
}
static TElement *getPtr(std::shared_ptr<TElement> &obj) {
return obj.get();
}
static TElement *getPtr(std::weak_ptr<TElement> &obj) {
if (auto ptr = obj.lock())
return ptr.get();
return nullptr;
}
static constexpr PointerOwnershipType getOwnership() {
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
? PointerOwnershipType::Owner
: std::is_same<std::shared_ptr<TElement>, T>::value
? PointerOwnershipType::SharedOwner
: PointerOwnershipType::SharedObserver;
}
static void clear(T &obj) {
obj.reset();
}
static void assign(T &obj, TElement *valuePtr) {
obj.reset(valuePtr);
}
//this is used, when old object exists and is the same type
static std::unique_ptr<pointer_utils::PointerSharedStateBase> saveToSharedState(T &obj) {
auto state = new SharedPtrSharedState{};
//to work with weak_ptr and shared_ptr create new std::shared_ptr
state->obj = std::shared_ptr<TElement>(obj);
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
//this is used, when old object doesn't exists or is not the same type
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedState(TElement *valuePtr) {
auto state = new SharedPtrSharedState{};
state->obj = std::shared_ptr<TElement>(valuePtr);
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static void loadFromSharedState(pointer_utils::PointerSharedStateBase *ctx, T &obj) {
auto state = dynamic_cast<SharedPtrSharedState *>(ctx);
//reinterpret_pointer_cast is only since c++17
auto p = reinterpret_cast<TElement *>(state->obj.get());
obj = std::shared_ptr<TElement>(state->obj, p);
}
};
}
template<typename RTTI>
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
//helper type for convienience
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
}
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T> {
using TValue = typename T::element_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
}
}
#endif //BITSERY_EXT_STD_SMART_PTR_H

View File

@@ -67,9 +67,9 @@ namespace bitsery {
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdStack, T> {
using TValue = typename T::value_type;
template<typename T, typename Seq>
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;

View File

@@ -0,0 +1,81 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_TUPLE_H
#define BITSERY_EXT_STD_TUPLE_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include <tuple>
namespace bitsery {
namespace ext {
template<typename ...Overloads>
class StdTuple : public details::CompositeTypeOverloadsUtils<std::tuple, Overloads...> {
public:
template<typename Ser, typename Writer, typename Fnc, typename ...Ts>
void serialize(Ser& ser, Writer&, const std::tuple<Ts...>& obj, Fnc&&) const {
serializeAll(ser, const_cast<std::tuple<Ts...>&>(obj));
}
template<typename Des, typename Reader, typename Fnc, typename ...Ts>
void deserialize(Des& des, Reader&, std::tuple<Ts...>& obj, Fnc&&) const {
serializeAll(des, obj);
}
private:
template<typename S, typename ...Ts>
void serializeAll(S& s, std::tuple<Ts...>& obj) const {
this->execAll(obj, [this, &s](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(s, std::get<Index>(data));
});
}
};
// deduction guide
template<typename ...Overloads>
StdTuple(Overloads...) -> StdTuple<Overloads...>;
}
namespace traits {
template<typename Tuple, typename ... Overloads>
struct ExtensionTraits<ext::StdTuple<Overloads...>, Tuple> {
static_assert(bitsery::details::IsSpecializationOf<Tuple, std::tuple>::value,
"StdTuple only works with std::tuple");
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_TUPLE_H

View File

@@ -0,0 +1,91 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_VARIANT_H
#define BITSERY_EXT_STD_VARIANT_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include <variant>
namespace bitsery {
namespace ext {
template<typename ...Overloads>
class StdVariant : public details::CompositeTypeOverloadsUtils<std::variant, Overloads...> {
public:
template<typename Ser, typename Writer, typename Fnc, typename ...Ts>
void serialize(Ser& ser, Writer& writer, const std::variant<Ts...>& obj, Fnc&&) const {
auto index = obj.index();
assert(index != std::variant_npos);
details::writeSize(writer, index);
this->execIndex(index, const_cast<std::variant<Ts...>&>(obj), [this, &ser](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(ser, std::get<Index>(data));
});
}
template<typename Des, typename Reader, typename Fnc, typename ...Ts>
void deserialize(Des& des, Reader& reader, std::variant<Ts...>& obj, Fnc&&) const {
size_t index{};
details::readSize(reader, index, sizeof...(Ts));
this->execIndex(index, obj, [this, &des](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
using TElem = typename std::variant_alternative<Index, std::variant<Ts...>>::type;
TElem item = ::bitsery::Access::create<TElem>();
this->serializeType(des, item);
data = std::variant<Ts...>(std::in_place_index_t<Index>{}, std::move(item));
});
}
};
// deduction guide
template<typename ...Overloads>
StdVariant(Overloads...) -> StdVariant<Overloads...>;
}
//defines empty fuction, that handles monostate
template <typename S>
void serialize(S& , std::monostate&) {}
namespace traits {
template<typename Variant, typename ... Overloads>
struct ExtensionTraits<ext::StdVariant<Overloads...>, Variant> {
static_assert(bitsery::details::IsSpecializationOf<Variant, std::variant>::value,
"StdVariant only works with std::variant");
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_VARIANT_H

View File

@@ -0,0 +1,136 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
#define BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
#include "../../details/serialization_common.h"
#include <functional>
#if __cplusplus < 201703L
#error these utils requires c++17
// in theory, it could be implemented using C++11
// but without class template argument deduction guides that would be very inconvenient to use
// these are very helpul for sum types (e.g. std::variant),
// but for product types (e.g. std::tuple) you can you can easily do it your self with lambda, without extension
#endif
namespace bitsery {
namespace ext {
// might be usable, when you want to have one overload set for different composite types,
// e.g. variant, tuple and pair
template<class... Ts>
struct CompositeTypeOverloads : Ts ... {
using Ts::operator()...;
};
template<typename ...Overloads>
CompositeTypeOverloads(Overloads...) -> CompositeTypeOverloads<Overloads...>;
// convenient way to invoke s.value<N>, shorter than specifying a lambda
template<typename T, size_t N>
struct OverloadValue {
template <typename S>
void operator()(S& s, T& v) const {
s.template value<N>(v);
}
};
// convenient way to invoke other extension using value or object overloads
// there is no reason to write OverloadExtLambda,
// because you'll need to specify lambda type, which is very inconvenient and it will be much
// easier to simple write a lambda with extension inside it,
// in order to implement it in a convenient way, i need a way to deduce only last template parameter (lambda type)
// but this is not possible with deduction guides at the moment
template<typename T, size_t N, typename Ext>
struct OverloadExtValue : public Ext {
template <typename S>
void operator()(S& s, T& v) const {
s.template ext<N>(v, static_cast<const Ext&>(*this));
}
};
template<typename T, typename Ext>
struct OverloadExtObject : public Ext {
template <typename S>
void operator()(S& s, T& v) const {
s.ext(v, static_cast<const Ext&>(*this));
}
};
}
namespace details {
template<template<typename ...> typename CompositeType, typename ...Overloads>
class CompositeTypeOverloadsUtils : public ext::CompositeTypeOverloads<Overloads...> {
protected:
// converts run-time index to compile-time index,
// by calling lambda with std::integral_constant<size_t, INDEX>
template<typename Fnc, typename ... Ts>
void execIndex(size_t index, CompositeType<Ts...>& obj, Fnc&& fnc) const {
execIndexImpl(index, obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// call lambda for all indexes in composite type
template<typename Fnc, typename ... Ts>
void execAll(CompositeType<Ts...>& obj, Fnc&& fnc) const {
execAllImpl(obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// serialize a type, by using overload first
template<typename S, typename T>
void serializeType(S& s, T& v) const {
// first check if overload exists, otherwise try to call serialize method
if constexpr (hasOverload<S, T>()) {
std::invoke(*this, s, v);
} else {
static_assert(details::SerializeFunction<S, T>::isDefined(),
"Please define overload or 'serialize' function for your type.");
s.object(v);
}
}
private:
template<typename S, typename T>
static constexpr bool hasOverload() {
return std::is_invocable<ext::CompositeTypeOverloads<Overloads...>,
std::add_lvalue_reference_t<S>, std::add_lvalue_reference_t<T>>::value;
}
template<typename Variant, typename Fnc, size_t ...Is>
void execIndexImpl(size_t index, Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
((index == Is ? fnc(obj, std::integral_constant<size_t, Is>{}), 0 : 0), ...);
}
template<typename Variant, typename Fnc, size_t ...Is>
void execAllImpl(Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
(fnc(obj, std::integral_constant<size_t, Is>{}), ...);
}
};
}
}
#endif //BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H

View File

@@ -1,6 +1,24 @@
//MIT License
//
// Created by fraillt on 17.11.30.
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_POINTER_UTILS_H
#define BITSERY_POINTER_UTILS_H
@@ -8,31 +26,110 @@
#include <unordered_map>
#include <vector>
#include <memory>
#include "polymorphism_utils.h"
#include <algorithm>
#include <cassert>
#include "../../details/adapter_utils.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {
//change name
enum class PointerType {
Nullable,
NotNull
};
// Observer - not responsible for pointer lifetime management.
// Owner - only ONE owner is responsible for this pointers creation/destruction
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer creation/destruction
// requires additional context to manage shared owners themselves.
// SharedOwner actually manages life time e.g. std::shared_ptr
// SharedObserver do not manage life time of the pointer, but can observe shared state .e.. std::weak_ptr
// and differently from Observer, creates new object if necessary and saves to shared state
enum class PointerOwnershipType : uint8_t {
//is not responsible for pointer lifetime management.
Observer,
//only ONE owner is responsible for this pointers creation/destruction
Owner,
//MANY shared owners is responsible for pointer creation/destruction
//requires additional context to manage shared owners themselves.
Shared
Observer,
Owner,
SharedOwner,
SharedObserver
};
//forward declaration
class PointerLinkingContext;
namespace utils {
namespace pointer_utils {
//this class is used to store context for shared ptr owners
struct PointerSharedStateBase {
virtual ~PointerSharedStateBase() = default;
};
//PLC info is internal classes for serializer, and deserializer
struct PLCInfo {
explicit PLCInfo(PointerOwnershipType ownershipType_)
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
PointerOwnershipType ownershipType;
bool isSharedProcessed;
void update(PointerOwnershipType ptrType) {
//do nothing for observer
if (ptrType == PointerOwnershipType::Observer)
return;
if (ownershipType == PointerOwnershipType::Observer) {
//set ownership type
ownershipType = ptrType;
return;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::SharedOwner || ptrType == PointerOwnershipType::SharedObserver);
//check if need to update to SharedOwner
if (ptrType == PointerOwnershipType::SharedOwner)
ownershipType = ptrType;
//mark that object already processed, so we do not serialize/deserialize duplicate objects
isSharedProcessed = true;
}
};
struct PLCInfoSerializer: PLCInfo {
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_), id{id_} {}
size_t id;
};
struct PLCInfoDeserializer : PLCInfo {
PLCInfoDeserializer(void *ptr, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_),
ownerPtr{ptr} {};
//need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
PLCInfoDeserializer& operator =(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer& operator =(PLCInfoDeserializer&&) = default;
void processOwner(void *ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto &o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void *(&ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.emplace_back(ptr);
}
}
void *ownerPtr;
std::vector<std::reference_wrapper<void *>> observersList{};
std::unique_ptr<PointerSharedStateBase> sharedState{};
};
class PointerLinkingContextSerialization {
public:
explicit PointerLinkingContextSerialization()
@@ -49,35 +146,14 @@ namespace bitsery {
~PointerLinkingContextSerialization() = default;
struct PointerInfo {
PointerInfo(size_t id_, PointerOwnershipType ownershipType_)
: id{id_},
ownershipType{ownershipType_},
sharedCount{0} {};
size_t id;
PointerOwnershipType ownershipType;
size_t sharedCount;
};
const PointerInfo &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PointerInfo{_currId + 1u, ptrType});
const PLCInfoSerializer &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{_currId + 1u, ptrType});
auto &ptrInfo = res.first->second;
if (res.second) {
++_currId;
return ptrInfo;
}
//ptr already exists
//for observer return success
if (ptrType == PointerOwnershipType::Observer)
return ptrInfo;
//set owner and return success
if (ptrInfo.ownershipType == PointerOwnershipType::Observer) {
ptrInfo.ownershipType = ptrType;
return ptrInfo;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::Shared);
ptrInfo.sharedCount++;
ptrInfo.update(ptrType);
return ptrInfo;
}
@@ -85,22 +161,18 @@ namespace bitsery {
//we cannot serialize pointers, if we haven't serialized objects themselves
bool isPointerSerializationValid() const {
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
[](const std::pair<const void *, PointerInfo> &p) {
return p.second.ownershipType != PointerOwnershipType::Observer;
[](const std::pair<const void *, PLCInfoSerializer> &p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
private:
size_t _currId;
std::unordered_map<const void *, PointerInfo> _ptrMap;
std::unordered_map<const void *, PLCInfoSerializer> _ptrMap;
};
//this class is used to store context for shared ptr owners
struct PointerSharedContextBase {
virtual ~PointerSharedContextBase() = default;
};
class PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContextDeserialization()
@@ -116,198 +188,194 @@ namespace bitsery {
~PointerLinkingContextDeserialization() = default;
struct PointerInfo {
PointerInfo(size_t id_, void *ptr, PointerOwnershipType ownershipType_)
: id{id_},
ownershipType{ownershipType_},
ownerPtr{ptr},
observersList{},
sharedContext{} {};
PointerInfo(const PointerInfo &) = delete;
PointerInfo &operator=(const PointerInfo &) = delete;
PointerInfo(PointerInfo &&) = default;
PointerInfo &operator=(PointerInfo &&) = default;
~PointerInfo() = default;
void processOwner(void *ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto &o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void *(&ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.push_back(ptr);
}
}
size_t id;
PointerOwnershipType ownershipType;
void *ownerPtr;
std::vector<std::reference_wrapper<void *>> observersList;
std::unique_ptr<PointerSharedContextBase> sharedContext;
};
PointerInfo &getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PointerInfo{id, nullptr, ptrType});
PLCInfoDeserializer &getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType});
auto &ptrInfo = res.first->second;
if (!res.second) {
assert(ptrType != PointerOwnershipType::Owner ||
ptrInfo.ownershipType == PointerOwnershipType::Observer);
if (ptrInfo.ownershipType == PointerOwnershipType::Observer)
ptrInfo.ownershipType = ptrType;
}
if (!res.second)
ptrInfo.update(ptrType);
return ptrInfo;
}
void clearSharedState() {
for (auto &item: _idMap)
item.second.sharedState.reset();
}
//valid, when all pointers has owners
bool isPointerDeserializationValid() const {
return std::all_of(_idMap.begin(), _idMap.end(), [](const std::pair<const size_t, PointerInfo> &p) {
return p.second.ownershipType != PointerOwnershipType::Observer;
});
return std::all_of(_idMap.begin(), _idMap.end(),
[](const std::pair<const size_t, PLCInfoDeserializer> &p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
private:
std::unordered_map<size_t, PointerInfo> _idMap;
std::unordered_map<size_t, PLCInfoDeserializer> _idMap;
};
template<template<typename> class Config>
class PointerOwnerManager {
template <typename TObject>
using Handler = typename Config<TObject>::Handler;
template<typename TObject>
struct HelperTypes {
using RTTI = typename Config<TObject>::RTTI;
using THandler = Handler<TObject>;
using TValue = typename std::remove_pointer<typename THandler::TPointer>::type;
};
template<typename Ser, typename T, typename Fnc>
void serializeImpl(PointerLinkingContextSerialization &, Ser &ser, const T &obj, Fnc &&,
std::true_type) const {
InheritanceTreeSerialize<Ser, T, HelperTypes> tree{};
auto handler = tree.getHandler(obj);
assert(handler.second);
ser.object(handler.first);
handler.second->process(ser, const_cast<T &>(obj));
}
template<typename Ser, typename T, typename Fnc>
void serializeImpl(PointerLinkingContextSerialization &, Ser &, const T &obj, Fnc &&fnc,
std::false_type) const {
auto handler = Handler<T>{};
fnc(*handler.getPtr(const_cast<T &>(obj)));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization &, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type) const {
InheritanceTreeTypeId id{};
des.object(id);
InheritanceTreeDeserialize<Des, T, HelperTypes> tree{};
auto handler = tree.getHandler(id);
if (handler) {
handler->create(obj);
handler->process(des, obj);
} else {
r.setError(ReaderError::InvalidPointer);
}
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization &, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type) const {
using TValue = typename HelperTypes<T>::TValue;
auto handler = Handler<T>{};
if (auto ptr = handler.getPtr(obj)) {
fnc(*ptr);
} else {
handler.template create<TValue>(obj);
fnc(*handler.getPtr(obj));
}
}
PointerType _ptrType;
template<template<typename> class TPtrManager,
template<typename> class TPolymorphicContext, typename RTTI>
class PointerObjectExtensionBase {
public:
explicit PointerOwnerManager(PointerType ptrType = PointerType::Nullable) : _ptrType{ptrType} {}
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable) :
_ptrType{ptrType} {}
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
auto handler = Handler<T>{};
auto ptr = handler.getPtr(obj);
auto ptr = TPtrManager<T>::getPtr(const_cast<T &>(obj));
if (ptr) {
auto ctx = ser.template context<PointerLinkingContext>();
assert(ctx != nullptr);
auto &ptrInfo = ctx->getInfoByPtr(ptr, Config<T>::OwnershipType);
auto &ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
details::writeSize(w, ptrInfo.id);
if (ptrInfo.sharedCount == 0) {
serializeImpl(*ctx, ser, obj, std::forward<Fnc>(fnc),
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
serializeImpl(ser, ptr, std::forward<Fnc>(fnc), w, IsPolymorphic<T>{});
}
} else {
assert(_ptrType == PointerType::Nullable);
details::writeSize(w, 0);
}
}
template<typename Des, typename Reader, typename T, typename Fnc>
void deserialize(Des &des, Reader &r, T &obj, Fnc &&fnc) const {
size_t id{};
details::readSize(r, id, std::numeric_limits<size_t>::max());
auto handler = Handler<T>{};
if (id) {
auto ctx = des.template context<PointerLinkingContext>();
assert(ctx != nullptr);
auto &ptrInfo = ctx->getInfoById(id, Config<T>::OwnershipType);
//todo add deserialization checking
if (ptrInfo.ownershipType == PointerOwnershipType::Owner) {
deserializeImpl(*ctx, des, obj, std::forward<Fnc>(fnc), r,
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
ptrInfo.processOwner(handler.getPtr(obj));
} else {
if (!ptrInfo.sharedContext) {
deserializeImpl(*ctx, des, obj, std::forward<Fnc>(fnc), r,
std::is_polymorphic<typename HelperTypes<T>::TValue>{});
ptrInfo.processOwner(handler.getPtr(obj));
ptrInfo.sharedContext = Config<T>::createSharedContext(obj);
} else {
Config<T>::restoreFromSharedContext(obj, ptrInfo.sharedContext.get());
}
}
auto &ptrInfo = ctx->getInfoById(id, TPtrManager<T>::getOwnership());
deserializeImpl(ptrInfo, des, obj, std::forward<Fnc>(fnc), r, IsPolymorphic<T>{},
std::integral_constant<PointerOwnershipType, TPtrManager<T>::getOwnership()>{});
} else {
if (_ptrType == PointerType::Nullable && handler.getPtr(obj)) {
handler.destroy(obj);
if (_ptrType == PointerType::Nullable) {
TPtrManager<T>::clear(obj);
} else
r.setError(ReaderError::InvalidPointer);
}
}
private:
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
};
template<typename T>
const void *getBasePtr(const T *ptr) const {
// todo implement handling of types with virtual inheritance
// this is required to correctly track same object, when one object is derived and other is base class
// e.g. shared_ptr<Base> and weak_ptr<Derived> or pointer observer Base*
return ptr;
}
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
void serializeImpl(Ser &ser, TPtr &ptr, Fnc &&, Writer &w, std::true_type) const {
const auto &ctx = ser.template context<TPolymorphicContext<RTTI>>();
ctx->serialize(ser, w, *ptr);
}
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
void serializeImpl(Ser &, TPtr &ptr, Fnc &&fnc, Writer &, std::false_type) const {
fnc(*ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, this](typename TPtrManager<T>::TElement *valuePtr) {
TPtrManager<T>::assign(obj, valuePtr);
});
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(*ptr);
} else {
ptr = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
fnc(*ptr);
TPtrManager<T>::assign(obj, ptr);
}
ptrInfo.processOwner(ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
if (!sharedState) {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, &sharedState](typename TPtrManager<T>::TElement *valuePtr) {
sharedState = TPtrManager<T>::createSharedState(valuePtr);
});
if (!sharedState)
sharedState = TPtrManager<T>::saveToSharedState(obj);
}
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
if (!sharedState) {
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
fnc(*ptr);
sharedState = TPtrManager<T>::saveToSharedState(obj);
} else {
auto res = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
fnc(*res);
sharedState = TPtrManager<T>::createSharedState(res);
}
}
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorph>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&fnc,
Reader &r, isPolymorph polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedObserver>) const {
deserializeImpl(ptrInfo, des, obj, fnc, r, polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorphic>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&,
Reader &, isPolymorphic,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void *&>(TPtrManager<T>::getPtrRef(obj)));
}
PointerType _ptrType;
};
}
//this class is for convenience
class PointerLinkingContext :
public utils::PointerLinkingContextSerialization,
public utils::PointerLinkingContextDeserialization {
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContext() = default;
bool isValid() {
return isPointerSerializationValid() && isPointerDeserializationValid();
}

View File

@@ -1,225 +1,238 @@
//MIT License
//
// Created by fraillt on 17.11.3.
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_POLYMORPHISM_H
#define BITSERY_EXT_POLYMORPHISM_H
#include "../inheritance.h"
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
#include <unordered_map>
#include <functional>
#include <memory>
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {
namespace details_polymorphism {
//stores template types
template<typename ...>
struct List {
};
}
//specialize for your base class by deriving from DerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
//helper type, that contains list of types
template<typename ...>
struct PolymorphicClassesList {
};
//specialize for your base class by deriving from PolymorphicDerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
//e.g.
// template <> PolymorphicBase<Animal>: DerivedClasses<Dog, Cat>{};
// template <> PolymorphicBase<Dog>: DerivedClasses<Bulldog, GoldenRetriever> {};
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
// template <> PolymorphicBaseClass<Dog>: PolymorphicDerivedClasses<Bulldog, GoldenRetriever> {};
// IMPORTANT !!!
// although you can add all derivates to same base like this:
// SuperClass<Animal>:DerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
// it will not work when you try to serialize Dog*, because it will not find Bulldog and GoldenRetriever
template<typename TBase>
struct PolymorphicBaseClass {
using childs = details_polymorphism::List<>;
using Childs = PolymorphicClassesList<>;
};
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
//e.g.
// template <> SuperClass<Animal>: DerivedClasses<Dog, Cat>{};
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
template<typename T1, typename ... Tn>
struct DerivedClasses {
using childs = details_polymorphism::List<T1, Tn...>;
struct PolymorphicDerivedClasses {
using Childs = PolymorphicClassesList<T1, Tn...>;
};
namespace utils {
//this object will be used to serialize/deserialize polymorphic type id within inheritance tree from base class
struct InheritanceTreeTypeId {
uint16_t depth{};
uint16_t index{};
template <typename S>
void serialize(S& s) {
s.value2b(depth);
s.value2b(index);
class PolymorphicHandlerBase {
public:
virtual void *create() const = 0;
virtual void process(void *ser, void *obj) const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
class PolymorphicHandler : public PolymorphicHandlerBase {
public:
void *create() const final {
return toBase(::bitsery::Access::createInHeap<TDerived>());
}
void process(void *ser, void *obj) const final {
static_cast<TSerializer *>(ser)->object(*static_cast<TDerived *>(fromBase(obj)));
}
private:
void *fromBase(void *obj) const {
return RTTI::template cast<TBase, TDerived>(static_cast<TBase *>(obj));
}
void *toBase(void *obj) const {
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived *>(obj));
}
};
template<typename RTTI>
class PolymorphicContext {
private:
struct BaseToDerivedKey {
std::size_t baseHash;
std::size_t derivedHash;
bool operator==(const BaseToDerivedKey &other) const {
return baseHash == other.baseHash && derivedHash == other.derivedHash;
}
};
template <typename S, typename TObject>
struct PolymorphicObjectHandlerInterface {
virtual void process(S& s, TObject& obj) const = 0;
virtual void create(TObject& obj) const = 0;
virtual ~PolymorphicObjectHandlerInterface() = default;
struct BaseToDerivedKeyHashier {
size_t operator()(const BaseToDerivedKey &key) const {
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
}
};
}
namespace details_polymorphism {
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void add() {
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(typename THierarchy<TDerived>::Childs{});
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived, typename T1, typename ... Tn>
void addChilds(PolymorphicClassesList<T1, Tn...>) {
static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
//iterate through derived class hierarchy as well
add<TSerializer, THierarchy, T1, T1>();
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void addChilds(PolymorphicClassesList<>) {
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::false_type) {
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TDerived>()};
if (_baseToDerivedMap.emplace(key, std::unique_ptr<PolymorphicHandlerBase>(
new PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>{})).second)
_baseToDerivedArray[key.baseHash].push_back(key.derivedHash);
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::true_type) {
//cannot add abstract class
}
std::unordered_map<BaseToDerivedKey, std::unique_ptr<PolymorphicHandlerBase>, BaseToDerivedKeyHashier> _baseToDerivedMap{};
// this will allow convert from platform specific type information, to platform independent base->derived index
// this only works if all polymorphic relationships (PolymorphicBaseClass<TBase> -> PolymorphicDerivedClasses<TDerived...>)
// is equal between platforms.
std::unordered_map<size_t, std::vector<size_t>> _baseToDerivedArray{};
public:
void clear() {
_baseToDerivedMap.clear();
_baseToDerivedArray.clear();
}
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
[[deprecated("de/serializer instance is not required")]] void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
add<TSerializer, THierarchy, T1, T1>();
registerBasesList<TSerializer, THierarchy>(s, PolymorphicClassesList<Tn...>{});
}
template<typename TSerializer, template<typename> class THierarchy>
[[deprecated]] void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
}
// THierarchy is the name of class, that defines hierarchy
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
// https://github.com/fraillt/bitsery/issues/9
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) {
add<TSerializer, THierarchy, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
}
template<typename TSerializer, template<typename> class THierarchy>
void registerBasesList(PolymorphicClassesList<>) {
}
// optional method, in case you want to construct base class hierarchy your self
template <typename TSerializer, typename TBase, typename TDerived>
void registerSingleBaseBranch() {
static_assert(std::is_base_of<TBase, TDerived>::value, "TDerived must be derived from TBase");
static_assert(!std::is_abstract<TDerived>::value, "TDerived cannot be abstract");
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
}
template<typename S, typename TObject, typename TDerived, typename RTTI, typename ObjectHandler>
struct PolymorphicObjectHandlerBase : public utils::PolymorphicObjectHandlerInterface<S, TObject> {
void process(S &s, TObject &obj) const final {
s.object(dynamic_cast<TDerived &>(*_handler.getPtr(obj)));
};
template<typename Serializer, typename Writer, typename TBase>
void serialize(Serializer &ser, Writer &writer, TBase &obj) {
//get derived key
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)};
auto it = _baseToDerivedMap.find(key);
assert(it != _baseToDerivedMap.end());
void create(TObject &obj) const final {
auto ptr = _handler.getPtr(obj);
if (ptr && RTTI::get(*ptr) != RTTI::template get<TDerived>()) {
_handler.destroy(obj);
_handler.template create<TDerived>(obj);
} else {
if (ptr == nullptr)
_handler.template create<TDerived>(obj);
//convert derived hash to derived index, to make it work in cross-platform environment
auto &vec = _baseToDerivedArray.find(key.baseHash)->second;
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(),
key.derivedHash)));
details::writeSize(writer, derivedIndex);
//serialize
it->second->process(&ser, &obj);
}
template<typename Deserializer, typename Reader, typename TBase, typename TAssignFnc>
void deserialize(Deserializer &des, Reader &reader, TBase *obj, TAssignFnc assignFnc) {
size_t derivedIndex{};
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
auto baseToDerivedVecIt = _baseToDerivedArray.find(RTTI::template get<TBase>());
//base class is known at compile time, so we can assert on this one
assert(baseToDerivedVecIt != _baseToDerivedArray.end());
if (baseToDerivedVecIt->second.size() > derivedIndex) {
//convert derived index to derived hash, to make it work in cross-platform environment
auto derivedHash = baseToDerivedVecIt->second[derivedIndex];
auto &handler = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), derivedHash})->second;
//if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
obj = static_cast<TBase *>(handler->create());
assignFnc(obj);
}
}
ObjectHandler _handler{};
};
template<typename S, typename TObject, template<typename> class TObjectManager>
class PolymorphicHandlersGenerator {
public:
template<typename Fnc>
static void generate(Fnc &&addHandlerFnc) {
PolymorphicHandlersGenerator tmp{std::forward<Fnc>(addHandlerFnc)};
}
private:
using RTTI = typename TObjectManager<TObject>::RTTI;
template <typename TDerived>
using TPolymorphicObjectHandler = PolymorphicObjectHandlerBase<S, TObject, TDerived, RTTI, typename TObjectManager<TObject>::THandler>;
template<typename Fnc>
explicit PolymorphicHandlersGenerator(Fnc &&addHandlerFnc):_addHandler(std::forward<Fnc>(addHandlerFnc)) {
//fill inheritance tree
using TBase = typename TObjectManager<TObject>::TValue;
add<TBase>();
}
template<typename TClass>
void add() {
addClass<TClass>();
//save current index and increase depth
auto saveIndex = index;
index = 0;
depth++;
addChilds<TClass>(typename PolymorphicBaseClass<TClass>::childs{});
//restore index and depth
depth--;
index = saveIndex;
}
template<typename TClassBase, typename T1, typename ... Tn>
void addChilds(details_polymorphism::List<T1, Tn...>) {
static_assert(std::is_base_of<TClassBase, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
add<T1>();
addChilds<TClassBase>(details_polymorphism::List<Tn...>{});
}
template<typename>
void addChilds(details_polymorphism::List<>) {
}
template<typename TClass>
void addClass() {
if (!std::is_abstract<TClass>::value) {
utils::InheritanceTreeTypeId tid{};
tid.index = index;
tid.depth = depth;
#if __cplusplus > 201103L
auto handler = std::make_unique<TPolymorphicObjectHandler<TClass>>();
#else
auto handler = std::unique_ptr<TPolymorphicObjectHandler<TClass>>(
new TPolymorphicObjectHandler<TClass>{});
#endif
if (_addHandler(RTTI::template get<TClass>(), tid, std::move(handler)))
++index;
}
}
std::function<bool(size_t, utils::InheritanceTreeTypeId,
std::unique_ptr<utils::PolymorphicObjectHandlerInterface<S, TObject>> &&)> _addHandler;
uint16_t depth{};
uint16_t index{};
};
}
namespace utils {
template<typename Ser, typename TObject, template<typename> class TObjectManager>
class InheritanceTreeSerialize {
public:
InheritanceTreeSerialize() {
details_polymorphism::PolymorphicHandlersGenerator<Ser, TObject, TObjectManager>::generate(
[this](size_t typeHash, InheritanceTreeTypeId id,
std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>> &&handler) {
return _map.emplace(std::make_pair(typeHash, std::make_pair(id, std::move(handler)))).second;
}
);
}
const std::pair<InheritanceTreeTypeId, PolymorphicObjectHandlerInterface<Ser, TObject>*> getHandler(const TObject &obj) {
using RTTI = typename TObjectManager<TObject>::RTTI;
auto handler = typename TObjectManager<TObject>::THandler{};
auto it = _map.find(RTTI::get(*handler.getPtr(obj)));
if (it != _map.end()) {
return std::make_pair(it->second.first, it->second.second.get());
}
return {};
}
private:
std::unordered_map<size_t, std::pair<InheritanceTreeTypeId, std::unique_ptr<PolymorphicObjectHandlerInterface<Ser, TObject>>>> _map{};
};
template<typename Des, typename TObject, template<typename> class TObjectManager>
class InheritanceTreeDeserialize {
public:
InheritanceTreeDeserialize() {
details_polymorphism::PolymorphicHandlersGenerator<Des, TObject, TObjectManager>::generate(
[this](size_t, InheritanceTreeTypeId id, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>> &&handler) {
return _map.emplace(std::make_pair(getHashFromId(id), std::move(handler))).second;
}
);
}
const PolymorphicObjectHandlerInterface<Des, TObject> *getHandler(const InheritanceTreeTypeId &id) {
auto it = _map.find(getHashFromId(id));
if (it != _map.end())
return it->second.get();
return nullptr;
}
private:
size_t getHashFromId(const InheritanceTreeTypeId &id) const {
size_t res = id.depth << 16;
return res + id.index;
}
std::unordered_map<size_t, std::unique_ptr<PolymorphicObjectHandlerInterface<Des, TObject>>> _map{};
};
}
handler->process(&des, obj);
} else
reader.setError(ReaderError::InvalidPointer);
}
};
}
}
#endif //BITSERY_EXT_POLYMORPHISM_H
#endif //BITSERY_EXT_POLYMORPHISM_UTILS_H

View File

@@ -1,27 +1,64 @@
//MIT License
//
// Created by fraillt on 17.11.30.
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_RTTI_UTILS_H
#define BITSERY_RTTI_UTILS_H
#include <typeinfo>
#include <type_traits>
#include <cstddef>
namespace bitsery {
namespace ext {
namespace utils {
struct StandardRTTI {
template<typename T>
static size_t get() {
return typeid(T).hash_code();
}
template<typename T>
static size_t get(T &&obj) {
return typeid(obj).hash_code();
}
};
}
struct StandardRTTI {
// static_assert(!std::is_pointer<TBase>::value &&
// !std::is_const<TBase>::value &&
// !std::is_volatile<TBase>::value, "");
template<typename TBase>
static size_t get(TBase &obj) {
return typeid(obj).hash_code();
}
template<typename TBase>
static constexpr size_t get() {
return typeid(TBase).hash_code();
}
template<typename TBase, typename TDerived>
static constexpr TDerived *cast(TBase *obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived *>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return std::is_polymorphic<TBase>::value;
}
};
}
}

View File

@@ -96,7 +96,8 @@ namespace bitsery {
constexpr RangeSpec(T minValue, T maxValue, T precision) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>({}, ((max - min) / precision))} {
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>(
{}, static_cast<details::SameSizeUnsigned<T>>((max - min) / precision))} {
}
@@ -163,7 +164,8 @@ namespace bitsery {
public:
template<typename ... Args>
explicit constexpr ValueRange(Args &&... args):_range{std::forward<Args>(args)...} {}
constexpr ValueRange(const TValue& min, const TValue& max, Args &&... args)
:_range{min, max, std::forward<Args>(args)...} {}
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &, Writer &writer, const T &v, Fnc &&) const {

View File

@@ -0,0 +1,40 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_FLEXIBLE_TYPE_STD_CHRONO_H
#define BITSERY_FLEXIBLE_TYPE_STD_CHRONO_H
#include "../ext/std_chrono.h"
namespace bitsery {
template<typename S, typename T, typename P>
void serialize(S &s, std::chrono::duration<T, P> &obj) {
s.template ext<sizeof(T)>(obj, ext::StdDuration{});
}
template<typename S, typename C, typename T, typename P>
void serialize(S &s, std::chrono::time_point<C, std::chrono::duration<T, P>> &obj) {
s.template ext<sizeof(T)>(obj, ext::StdTimePoint{});
}
}
#endif //BITSERY_FLEXIBLE_TYPE_STD_CHRONO_H

View File

@@ -28,8 +28,8 @@
#include "../details/flexible_common.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::deque<TArgs... > &obj) {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::deque<T, Allocator> &obj) {
flexible::processContainer(s, obj);
}
}

View File

@@ -28,8 +28,8 @@
#include "../details/flexible_common.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::forward_list<TArgs... > &obj) {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::forward_list<T, Allocator> &obj) {
flexible::processContainer(s, obj);
}
}

View File

@@ -28,8 +28,8 @@
#include "../details/flexible_common.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::list<TArgs... > &obj) {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::list<T, Allocator> &obj) {
flexible::processContainer(s, obj);
}
}

View File

@@ -28,23 +28,19 @@
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TKey = typename std::map<TArgs...>::key_type;
using TValue = typename std::map<TArgs...>::mapped_type;
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::map<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[&s](TKey& key, TValue& value) {
[&s](Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename ... TArgs>
void serialize(S &s, std::multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TKey = typename std::multimap<TArgs...>::key_type;
using TValue = typename std::multimap<TArgs...>::mapped_type;
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::multimap<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[&s](TKey& key, TValue& value) {
[&s](Key& key, T& value) {
s.object(key);
s.object(value);
});

View File

@@ -0,0 +1,45 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H
#define BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H
#include "../ext/std_smart_ptr.h"
namespace bitsery {
template<typename S, typename T, typename D>
void serialize(S &s, std::unique_ptr<T, D> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::shared_ptr<T> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::weak_ptr<T> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
}
#endif //BITSERY_FLEXIBLE_TYPE_STD_MEMORY_H

View File

@@ -28,13 +28,13 @@
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::set<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
template<typename S, typename ... TArgs>
void serialize(S &s, std::multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::multiset<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}

View File

@@ -28,8 +28,8 @@
#include "../details/flexible_common.h"
namespace bitsery {
template<typename S, typename T, typename ... TArgs>
void serialize(S &s, std::basic_string<T, TArgs...> &str) {
template<typename S, typename CharT, typename Traits, typename Allocator>
void serialize(S &s, std::basic_string<CharT, Traits, Allocator> &str) {
flexible::processContainer(s, str);
}
}

View File

@@ -0,0 +1,35 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_FLEXIBLE_TYPE_STD_TUPLE_H
#define BITSERY_FLEXIBLE_TYPE_STD_TUPLE_H
#include "../ext/std_tuple.h"
namespace bitsery {
template<typename S, typename ...Ts>
void serialize(S &s, std::tuple<Ts...> &obj) {
s.ext(obj, ext::StdTuple{});
}
}
#endif //BITSERY_FLEXIBLE_TYPE_STD_TUPLE_H

View File

@@ -28,23 +28,19 @@
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::unordered_map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TKey = typename std::unordered_map<TArgs...>::key_type;
using TValue = typename std::unordered_map<TArgs...>::mapped_type;
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_map<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[&s](TKey& key, TValue& value) {
[&s](Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename ... TArgs>
void serialize(S &s, std::unordered_multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TKey = typename std::unordered_multimap<TArgs...>::key_type;
using TValue = typename std::unordered_multimap<TArgs...>::mapped_type;
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[&s](TKey& key, TValue& value) {
[&s](Key& key, T& value) {
s.object(key);
s.object(value);
});

View File

@@ -28,13 +28,13 @@
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::unordered_set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_set<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
template<typename S, typename ... TArgs>
void serialize(S &s, std::unordered_multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multiset<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}

View File

@@ -0,0 +1,35 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_FLEXIBLE_TYPE_STD_VARIANT_H
#define BITSERY_FLEXIBLE_TYPE_STD_VARIANT_H
#include "../ext/std_variant.h"
namespace bitsery {
template<typename S, typename ...Ts>
void serialize(S &s, std::variant<Ts...> &obj) {
s.ext(obj, ext::StdVariant{});
}
}
#endif //BITSERY_FLEXIBLE_TYPE_STD_VARIANT_H

View File

@@ -28,8 +28,8 @@
#include "../details/flexible_common.h"
namespace bitsery {
template<typename S, typename ... TArgs>
void serialize(S &s, std::vector<TArgs... > &obj) {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::vector<T, Allocator> &obj) {
flexible::processContainer(s, obj);
}
}

View File

@@ -432,7 +432,7 @@ namespace bitsery {
template <typename T>
size_t quickMeasureSize(const T& value) {
BasicSerializer<MeasureSize> ser {nullptr};
BasicSerializer<MeasureSize> ser{MeasureSize{}};
ser.object(value);
auto& w = AdapterAccess::getWriter(ser);
w.flush();

View File

@@ -24,7 +24,7 @@
#define BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#include "traits.h"
#include <iostream>
#include "../../details/serialization_common.h"
namespace bitsery {
namespace traits {
@@ -53,13 +53,28 @@ namespace bitsery {
return container.size();
}
static void resize(T& container, size_t size) {
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
static void resizeImpl(T& container, size_t size, std::true_type) {
container.resize(size);
}
static void resizeImpl(T& container, size_t newSize, std::false_type) {
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_back(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
container.erase(std::next(std::begin(container), newSize));
}
}
};
template <typename T, bool Resizable = ContainerTraits<T>::isResizable>
struct StdContainerForBufferAdapter {
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
};
@@ -73,9 +88,10 @@ namespace bitsery {
auto newSize = static_cast<size_t>(container.size() * 1.5 + 128);
//make data cache friendly
newSize -= newSize % 64;//64 is cache line size
container.resize(std::max(newSize, container.capacity()));
container.resize((std::max)(newSize, container.capacity()));
}
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
};

View File

@@ -155,6 +155,7 @@ namespace bitsery {
}
using TIterator = details::NotDefinedType;
using TConstIterator = details::NotDefinedType;
using TValue = typename ContainerTraits<T>::TValue;
};
@@ -162,6 +163,7 @@ namespace bitsery {
template <typename T, size_t N>
struct BufferAdapterTraits<T[N]> {
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;
};
@@ -169,12 +171,14 @@ namespace bitsery {
template <typename T>
struct BufferAdapterTraits<const T*> {
using TIterator = const T*;
using TConstIterator = const T*;
using TValue = T;
};
template <typename T>
struct BufferAdapterTraits<T*> {
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;
};

View File

@@ -31,9 +31,9 @@ namespace bitsery {
namespace traits {
template<typename ... TArgs>
struct ContainerTraits<std::deque<TArgs...>>
: public StdContainer<std::deque<TArgs...>, true, false> {};
template<typename T, typename Allocator>
struct ContainerTraits<std::deque<T, Allocator>>
: public StdContainer<std::deque<T, Allocator>, true, false> {};
}

View File

@@ -24,23 +24,42 @@
#ifndef BITSERY_TRAITS_STD_FORWARD_LIST_H
#define BITSERY_TRAITS_STD_FORWARD_LIST_H
#include "core/traits.h"
#include "../details/serialization_common.h"
#include <forward_list>
namespace bitsery {
namespace traits {
template<typename ... TArgs>
struct ContainerTraits<std::forward_list<TArgs...>> {
using TValue = typename std::forward_list<TArgs...>::value_type;
template<typename T, typename Allocator>
struct ContainerTraits<std::forward_list<T, Allocator>> {
using TValue = T;
static constexpr bool isResizable = true;
static constexpr bool isContiguous = false;
static size_t size(const std::forward_list<TArgs...>& container) {
static size_t size(const std::forward_list<T, Allocator>& container) {
return static_cast<size_t>(std::distance(container.begin(), container.end()));
}
static void resize(std::forward_list<TArgs...>& container, size_t size) {
static void resize(std::forward_list<T, Allocator>& container, size_t size) {
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t size, std::true_type) {
container.resize(size);
}
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t newSize, std::false_type) {
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_front(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
//erase_after must have atleast one element to work
if (newSize > 0)
container.erase_after(std::next(std::begin(container), newSize-1));
else
container.clear();
}
}
};
}
}

View File

@@ -31,9 +31,9 @@ namespace bitsery {
namespace traits {
template<typename ... TArgs>
struct ContainerTraits<std::list<TArgs...>>
: public StdContainer<std::list<TArgs...>, true, false> {};
template<typename T, typename Allocator>
struct ContainerTraits<std::list<T, Allocator>>
: public StdContainer<std::list<T, Allocator>, true, false> {};
}

View File

@@ -33,18 +33,18 @@ namespace bitsery {
// specialization for string, because string is already included for std::char_traits
template<typename ... TArgs>
struct ContainerTraits<std::basic_string<TArgs...>>
:public StdContainer<std::basic_string<TArgs...>, true, true> {};
template<typename CharT, typename Traits, typename Allocator>
struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>>
:public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true> {};
template <typename ... TArgs>
struct TextTraits<std::basic_string<TArgs...>> {
using TValue = typename ContainerTraits<std::basic_string<TArgs...>>::TValue;
template <typename CharT, typename Traits, typename Allocator>
struct TextTraits<std::basic_string<CharT, Traits, Allocator>> {
using TValue = typename ContainerTraits<std::basic_string<CharT, Traits, Allocator>>::TValue;
//string is automatically null-terminated
static constexpr bool addNUL = false;
//is is not 100% accurate, but for performance reasons assume that string stores text, not binary data
static size_t length(const std::basic_string<TArgs...>& str) {
static size_t length(const std::basic_string<CharT, Traits, Allocator>& str) {
return str.size();
}
};
@@ -60,9 +60,9 @@ namespace bitsery {
}
};
template<typename ... TArgs>
struct BufferAdapterTraits<std::basic_string<TArgs...>>
:public StdContainerForBufferAdapter<std::basic_string<TArgs...>> {};
template<typename CharT, typename Traits, typename Allocator>
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
:public StdContainerForBufferAdapter<std::basic_string<CharT, Traits, Allocator>> {};
}

View File

@@ -30,18 +30,18 @@
namespace bitsery {
namespace traits {
template<typename ... TArgs>
struct ContainerTraits<std::vector<TArgs...>>
:public StdContainer<std::vector<TArgs...>, true, true> {};
template<typename T, typename Allocator>
struct ContainerTraits<std::vector<T, Allocator>>
:public StdContainer<std::vector<T, Allocator>, true, true> {};
//bool vector is not contiguous, do not copy it directly to buffer
template<typename Allocator>
struct ContainerTraits<std::vector<bool, Allocator>>
:public StdContainer<std::vector<bool, Allocator>, true, false> {};
template<typename ... TArgs>
struct BufferAdapterTraits<std::vector<TArgs...>>
:public StdContainerForBufferAdapter<std::vector<TArgs...>> {};
template<typename T, typename Allocator>
struct BufferAdapterTraits<std::vector<T, Allocator>>
:public StdContainerForBufferAdapter<std::vector<T, Allocator>> {};
}

View File

@@ -4,4 +4,3 @@ set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=bitsery")
set(CTEST_DROP_SITE_CDASH TRUE)

View File

@@ -6,7 +6,6 @@ set(CTEST_BINARY_DIRECTORY "build")
set(ENV{CXXFLAGS} "--coverage")
#when using Ninja generator, ctest_coverage cannot find files...
set(CTEST_CMAKE_GENERATOR "CodeBlocks - Unix Makefiles")
set(CTEST_USE_LAUNCHERS 1)
set(CTEST_COVERAGE_COMMAND "gcov")

4
scripts/show_coverage.sh Normal file → Executable file
View File

@@ -1,8 +1,8 @@
#!/bin/sh
BUILD_DIR=$1
BUILD_DIR=./build
TESTS_BUILD_DIR=$BUILD_DIR/tests/CMakeFiles/
COV_INFO=$TESTS_BUILD_DIR/bitsery_coverage.info
lcov --directory $TESTS_BUILD_DIR --capture --output-file $COV_INFO
lcov --extract $COV_INFO '*include/bitsery*' --output-file $COV_INFO.clean
genhtml --output-directory $TESTS_BUILD_DIR/coverage_web $COV_INFO.clean
xdg-open $TESTS_BUILD_DIR/coverage_web/index.html
x-www-browser $TESTS_BUILD_DIR/coverage_web/index.html

View File

@@ -20,7 +20,7 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.10)
project(bitsery_tests CXX)
find_package(GTest 1.8 REQUIRED)
@@ -31,19 +31,19 @@ endif()
file(GLOB TestSourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(WARNING "extension tests for optional is disable for VS, because VS currenty doesn't have <optional>")
list(REMOVE_ITEM TestSourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/serialization_ext_std_optional.cpp)
endif()
enable_testing()
foreach(TestFile ${TestSourceFiles})
foreach (TestFile ${TestSourceFiles})
get_filename_component(TestName ${TestFile} NAME_WE)
set(TestName bitsery.test.${TestName})
add_executable(${TestName} ${TestFile})
target_link_libraries(${TestName} PRIVATE GTest::Main Bitsery::bitsery)
add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++ -Wno-c++14-extensions)
endif()
gtest_discover_tests(${TestName})
# add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
endforeach()
#======================= setup development environment ====================
@@ -55,7 +55,7 @@ if (ParentDir)
file(GLOB_RECURSE HeadersForIDE ${ParentDir}/include/bitsery/*.h)
# create dummy target IDE
file(WRITE ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp "//generated by CMake to create dummy target with all includes for IDE.")
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp )
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp)
# add headers so IDE correctly show them
target_sources(bitsery.dummy_for_ide PRIVATE ${HeadersForIDE} serialization_test_utils.h)
target_link_libraries(bitsery.dummy_for_ide PRIVATE GTest::Main Bitsery::bitsery)

View File

@@ -20,13 +20,14 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include <bitsery/adapter/stream.h>
#include <bitsery/adapter_writer.h>
#include <bitsery/adapter_reader.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include <sstream>
//some helper types

View File

@@ -21,11 +21,11 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include <bitsery/adapter_writer.h>
#include <bitsery/adapter_reader.h>
#include <bitsery/ext/value_range.h>
#include "serialization_test_utils.h"
#include <gmock/gmock.h>
using testing::Eq;
using testing::ContainerEq;
@@ -56,19 +56,19 @@ using InverseReader = bitsery::AdapterReader<InputAdapter, InverseEndiannessConf
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
//fill initial values
IntegralTypes src{};
src.a = static_cast<int64_t>(0x1122334455667788);
src.b = 0xBBCCDDEE;
src.c = static_cast<int16_t>(0xCCDD);
src.d = static_cast<uint8_t>(0xDD);
src.e = static_cast<int8_t>(0xEE);
src.a = static_cast<int64_t>(0x1122334455667788u);
src.b = 0xBBCCDDEEu;
src.c = static_cast<int16_t>(0xCCDDu);
src.d = static_cast<uint8_t>(0xDDu);
src.e = static_cast<int8_t>(0xEEu);
//fill expected result after swap
IntegralTypes resInv{};
resInv.a = static_cast<int64_t>(0x8877665544332211);
resInv.b = 0xEEDDCCBB;
resInv.c = static_cast<int16_t>(0xDDCC);
resInv.d = static_cast<uint8_t>(0xDD);
resInv.e = static_cast<int8_t>(0xEE);
resInv.a = static_cast<int64_t>(0x8877665544332211u);
resInv.b = 0xEEDDCCBBu;
resInv.c = static_cast<int16_t>(0xDDCCu);
resInv.d = static_cast<uint8_t>(0xDDu);
resInv.e = static_cast<int8_t>(0xEEu);
//create and write to buffer
Buffer buf{};

View File

@@ -21,9 +21,10 @@
//SOFTWARE.
#include <bitsery/ext/value_range.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/value_range.h>
using testing::Eq;
using testing::ContainerEq;

View File

@@ -153,3 +153,28 @@ TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
EXPECT_THAT(r2[1], Eq(0u));
EXPECT_THAT(r3, Eq(0u));
}
TEST(DataReading, ConstBufferAllAdapters) {
//create and write to buffer
uint16_t data = 7549;
Buffer bufWrite{};
Writer bw{bufWrite};
bw.writeBytes<2>(data);
bw.flush();
const Buffer buf{bufWrite};
//read from buffer
using Adapter1 = bitsery::InputBufferAdapter<const Buffer>;
using Adapter2 = bitsery::UnsafeInputBufferAdapter<const Buffer>;
bitsery::AdapterReader<Adapter1, bitsery::DefaultConfig> r1{Adapter1{buf.begin(), buf.end()}};
bitsery::AdapterReader<Adapter2, bitsery::DefaultConfig> r2{Adapter2{buf.begin(), buf.end()}};
uint16_t res1{};
r1.readBytes<2>(res1);
uint16_t res2{};
r2.readBytes<2>(res2);
EXPECT_THAT(res1, Eq(data));
EXPECT_THAT(res2, Eq(data));
}

View File

@@ -20,9 +20,11 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using testing::Eq;
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;

View File

@@ -20,11 +20,12 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/details/serialization_common.h>
#include <bitsery/traits/array.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;

View File

@@ -21,10 +21,7 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/flexible.h>
#include <bitsery/flexible/string.h>
#include <bitsery/flexible/array.h>
#include <bitsery/flexible/vector.h>
@@ -37,6 +34,21 @@
#include <bitsery/flexible/unordered_map.h>
#include <bitsery/flexible/set.h>
#include <bitsery/flexible/unordered_set.h>
#include <bitsery/flexible/memory.h>
#include <bitsery/flexible/chrono.h>
#if __cplusplus > 201402L
#include <bitsery/flexible/tuple.h>
#include <bitsery/flexible/variant.h>
#else
#if defined(_MSC_VER)
#pragma message("tuple and variant only works with c++17")
#else
#warning "tuple and variant only works with c++17"
#endif
#endif
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
@@ -45,9 +57,9 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx{};
ctx.createSerializer().archive(ti,te,tf,td,tb);
ctx.createSerializer().archive(ti, te, tf, td, tb);
//result
int ri{};
@@ -55,7 +67,7 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
float rf{};
double rd{};
bool rb{};
ctx.createDeserializer().archive(ri,re,rf,rd,rb);
ctx.createDeserializer().archive(ri, re, rf, rd, rb);
//test
EXPECT_THAT(ri, Eq(ti));
@@ -70,9 +82,9 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx;
auto& ser = ctx.createSerializer();
auto &ser = ctx.createSerializer();
ser.object(ti);
ser.object(te);
ser.object(tf);
@@ -85,7 +97,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
float rf{};
double rd{};
bool rb{};
auto& des = ctx.createDeserializer();
auto &des = ctx.createDeserializer();
des.object(ri);
des.object(re);
des.object(rf);
@@ -105,9 +117,9 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx;
auto& ser = ctx.createSerializer();
auto &ser = ctx.createSerializer();
ser.value<sizeof(ti)>(ti);
ser.archive(te, tf, td);
ser.object(tb);
@@ -118,7 +130,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
float rf{};
double rd{};
bool rb{};
auto& des = ctx.createDeserializer();
auto &des = ctx.createDeserializer();
des.archive(ri, re, rf);
des.value8b(rd);
des.object(rb);
@@ -131,28 +143,27 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
EXPECT_THAT(rb, Eq(tb));
}
template <typename T>
template<typename T>
T procArchive(const T& testData) {
SerializationContext ctx;
ctx.createSerializer().archive(testData);
T res;
T res{};
ctx.createDeserializer().archive(res);
return res;
}
template <typename T>
template<typename T>
T procArchiveWithMaxSize(const T& testData) {
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::maxSize(testData, 100));
T res;
T res{};
ctx.createDeserializer().archive(bitsery::maxSize(res, 100));
return res;
}
TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
const int t1[3]{8748,-484,45};
int r1[3]{0,0,0};
const int t1[3]{8748, -484, 45};
int r1[3]{0, 0, 0};
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::asContainer(t1));
@@ -163,7 +174,7 @@ TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
TEST(FlexibleSyntax, CStyleArrayForIntegralTypesAsText) {
const char t1[3]{"hi"};
char r1[3]{0,0,0};
char r1[3]{0, 0, 0};
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::asText(t1));
@@ -183,7 +194,6 @@ TEST(FlexibleSyntax, CStyleArray) {
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
}
TEST(FlexibleSyntax, StdString) {
std::string t1{"my nice string"};
std::string t2{};
@@ -195,56 +205,51 @@ TEST(FlexibleSyntax, StdString) {
}
TEST(FlexibleSyntax, StdArray) {
std::array<int, 3> t1{8748,-484,45};
std::array<int, 3> t1{8748, -484, 45};
std::array<int, 0> t2{};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdVector) {
std::vector<int> t1{8748,-484,45};
std::vector<float> t2{5.f,0.198f};
std::vector<int> t1{8748, -484, 45};
std::vector<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdList) {
std::list<int> t1{8748,-484,45};
std::list<float> t2{5.f,0.198f};
std::list<int> t1{8748, -484, 45};
std::list<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdForwardList) {
std::forward_list<int> t1{8748,-484,45};
std::forward_list<float> t2{5.f,0.198f};
std::forward_list<int> t1{8748, -484, 45};
std::forward_list<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdDeque) {
std::deque<int> t1{8748,-484,45};
std::deque<float> t2{5.f,0.198f};
std::deque<int> t1{8748, -484, 45};
std::deque<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdQueue) {
@@ -254,7 +259,6 @@ TEST(FlexibleSyntax, StdQueue) {
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
}
TEST(FlexibleSyntax, StdPriorityQueue) {
@@ -272,7 +276,6 @@ TEST(FlexibleSyntax, StdPriorityQueue) {
r1.pop();
t1.pop();
}
}
TEST(FlexibleSyntax, StdStack) {
@@ -282,13 +285,12 @@ TEST(FlexibleSyntax, StdStack) {
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
}
TEST(FlexibleSyntax, StdUnorderedMap) {
std::unordered_map<int, int> t1;
t1.emplace(3423,624);
t1.emplace(-5484,-845);
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
@@ -296,9 +298,9 @@ TEST(FlexibleSyntax, StdUnorderedMap) {
TEST(FlexibleSyntax, StdUnorderedMultiMap) {
std::unordered_multimap<std::string, int> t1;
t1.emplace("one",624);
t1.emplace("two",-845);
t1.emplace("one",897);
t1.emplace("one", 624);
t1.emplace("two", -845);
t1.emplace("one", 897);
EXPECT_TRUE(procArchive(t1) == t1);
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
@@ -306,8 +308,8 @@ TEST(FlexibleSyntax, StdUnorderedMultiMap) {
TEST(FlexibleSyntax, StdMap) {
std::map<int, int> t1;
t1.emplace(3423,624);
t1.emplace(-5484,-845);
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
@@ -315,9 +317,9 @@ TEST(FlexibleSyntax, StdMap) {
TEST(FlexibleSyntax, StdMultiMap) {
std::multimap<std::string, int> t1;
t1.emplace("one",624);
t1.emplace("two",-845);
t1.emplace("one",897);
t1.emplace("one", 624);
t1.emplace("two", -845);
t1.emplace("one", 897);
auto res = procArchive(t1);
//same key values is not ordered, and operator == compares each element at same position
@@ -375,6 +377,54 @@ TEST(FlexibleSyntax, StdMultiSet) {
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
}
TEST(FlexibleSyntax, StdSmartPtr) {
std::shared_ptr<int> dataShared1(new int{4});
std::weak_ptr<int> dataWeak1(dataShared1);
std::unique_ptr<std::string> dataUnique1{new std::string{"hello world"}};
bitsery::ext::PointerLinkingContext plctx1{};
BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext> ctx;
ctx.createSerializer(&plctx1).archive(dataShared1, dataWeak1, dataUnique1);
std::shared_ptr<int> resShared1{};
std::weak_ptr<int> resWeak1{};
std::unique_ptr<std::string> resUnique1{};
ctx.createDeserializer(&plctx1).archive(resShared1, resWeak1, resUnique1);
//clear shared state from pointer linking context
plctx1.clearSharedState();
EXPECT_TRUE(plctx1.isValid());
EXPECT_THAT(*resShared1, Eq(*dataShared1));
EXPECT_THAT(*resWeak1.lock(), Eq(*dataWeak1.lock()));
EXPECT_THAT(*resUnique1, Eq(*dataUnique1));
}
TEST(FlexibleSyntax, StdDuration) {
std::chrono::duration<int64_t, std::milli> t1{54654};
EXPECT_TRUE(procArchive(t1) == t1);
}
TEST(FlexibleSyntax, StdTimePoint) {
using Duration = std::chrono::duration<double, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;
TP data{Duration{874656.4798}};
EXPECT_TRUE(procArchive(data) == data);
}
#if __cplusplus > 201402L
TEST(FlexibleSyntax, StdTuple) {
std::tuple<int, std::string, std::vector<char>> t1{5,"hello hello", {'A','B','C'}};
EXPECT_TRUE(procArchive(t1) == t1);
}
TEST(FlexibleSyntax, StdVariant) {
std::variant<float, std::string, std::chrono::milliseconds> t1{std::string("hello hello")};
EXPECT_TRUE(procArchive(t1) == t1);
}
#endif
TEST(FlexibleSyntax, NestedTypes) {
std::unordered_map<std::string, std::vector<std::string>> t1;

View File

@@ -0,0 +1,346 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <forward_list>
#include <bitsery/traits/forward_list.h>
#include <bitsery/ext/std_set.h>
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::ContainerEq;
using testing::Eq;
//forward declare, for testing with std::unordered_map
class HasherForNonDefaultConstructible;
class NonDefaultConstructible {
int32_t i{0};
friend class HasherForNonDefaultConstructible;
friend class bitsery::Access;
NonDefaultConstructible() = default;
template <typename S>
void serialize(S& s) {
s.value4b(i);
}
public:
explicit NonDefaultConstructible(int32_t v):i{v} {}
bool operator == (const NonDefaultConstructible& other) const {
return i == other.i;
}
bool operator < (const NonDefaultConstructible& other) const {
return i < other.i;
}
};
class HasherForNonDefaultConstructible {
public:
size_t operator()(const NonDefaultConstructible& o) const {
return std::hash<int32_t>()(o.i);
}
};
TEST(DeserializeNonDefaultConstructible, Container) {
SerializationContext ctx{};
std::vector<NonDefaultConstructible> data{};
data.emplace_back(1);
data.emplace_back(2);
data.emplace_back(3);
std::vector<NonDefaultConstructible> res{};
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);
EXPECT_THAT(res, ContainerEq(data));
}
//this test is here, because when object is not constructible we cannot simple "resize" container
TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
SerializationContext ctx{};
std::vector<NonDefaultConstructible> data{};
data.emplace_back(1);
std::vector<NonDefaultConstructible> res{};
res.emplace_back(2);
res.emplace_back(3);
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);
EXPECT_THAT(res, ContainerEq(data));
}
TEST(DeserializeNonDefaultConstructible, ResultStdForwardListShouldShrink) {
// forward list doesn't have .erase function, bet has erase_after
// in this case, if new size is 0 it must call clear, so we need to check two cases
{
// 1) when result should have more than 0 elements
SerializationContext ctx{};
std::forward_list<NonDefaultConstructible> data{};
data.push_front(NonDefaultConstructible{1});
std::forward_list<NonDefaultConstructible> res{};
res.push_front(NonDefaultConstructible{21});
res.push_front(NonDefaultConstructible{14});
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);
auto resIt = res.begin();
for (auto it = data.begin(); it != data.end(); ++it, ++resIt) {
EXPECT_THAT(*resIt, Eq(*it));
}
EXPECT_THAT(resIt, Eq(res.end()));
}
{
// 1) when result should have 0 elements
SerializationContext ctx{};
std::forward_list<NonDefaultConstructible> data{};
std::forward_list<NonDefaultConstructible> res{};
res.push_front(NonDefaultConstructible{1});
res.push_front(NonDefaultConstructible{14});
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);
EXPECT_THAT(res.begin(), Eq(res.end()));
}
{
// also check if correctly expands if source is bigger than destination
SerializationContext ctx{};
std::forward_list<NonDefaultConstructible> data{};
data.push_front(NonDefaultConstructible{1});
data.push_front(NonDefaultConstructible{14});
std::forward_list<NonDefaultConstructible> res{};
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);
auto resIt = res.begin();
for (auto it = data.begin(); it != data.end(); ++it, ++resIt) {
EXPECT_THAT(*resIt, Eq(*it));
}
EXPECT_THAT(resIt, Eq(res.end()));
}
}
TEST(DeserializeNonDefaultConstructible, StdSet) {
SerializationContext ctx{};
std::set<NonDefaultConstructible> data;
data.insert(NonDefaultConstructible{1});
data.insert(NonDefaultConstructible{2});
std::set<NonDefaultConstructible> res{};
data.insert(NonDefaultConstructible{3});
ctx.createSerializer().ext(data, bitsery::ext::StdSet{10});
ctx.createDeserializer().ext(res, bitsery::ext::StdSet{10});
EXPECT_THAT(res, ContainerEq(data));
}
TEST(DeserializeNonDefaultConstructible, StdMap) {
SerializationContext ctx{};
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> data;
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
std::unordered_map<NonDefaultConstructible, NonDefaultConstructible, HasherForNonDefaultConstructible> res{};
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
data.emplace(NonDefaultConstructible{4}, NonDefaultConstructible{4});
auto& ser = ctx.createSerializer();
ser.ext(data, bitsery::ext::StdMap{10},[&ser](NonDefaultConstructible& key, NonDefaultConstructible& value) {
ser.object(key);
ser.object(value);
});
auto& des = ctx.createDeserializer();
des.ext(res, bitsery::ext::StdMap{10},[&des](NonDefaultConstructible& key, NonDefaultConstructible& value) {
des.object(key);
des.object(value);
});
EXPECT_THAT(res, ContainerEq(data));
}
struct NonPolymorphicPointers {
NonDefaultConstructible* pp;
std::unique_ptr<NonDefaultConstructible> up;
std::shared_ptr<NonDefaultConstructible> sp;
std::weak_ptr<NonDefaultConstructible> wp;
};
template <typename S>
void serialize(S& s, NonPolymorphicPointers& o) {
s.ext(o.pp, bitsery::ext::PointerOwner{});
s.ext(o.up, bitsery::ext::StdSmartPtr{});
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
}
TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext>;
SerContext ctx{};
NonPolymorphicPointers data{};
data.pp = new NonDefaultConstructible{3};
data.up = std::unique_ptr<NonDefaultConstructible>(new NonDefaultConstructible{54});
data.sp = std::shared_ptr<NonDefaultConstructible>(new NonDefaultConstructible{-481});
data.wp = data.sp;
NonPolymorphicPointers res{};
bitsery::ext::PointerLinkingContext plctx1{};
ctx.createSerializer(&plctx1).object(data);
ctx.createDeserializer(&plctx1).object(res);
EXPECT_THAT(*res.pp, Eq(*data.pp));
delete res.pp;
delete data.pp;
EXPECT_THAT(*res.up, Eq(*data.up));
EXPECT_THAT(*res.sp, Eq(*data.sp));
EXPECT_THAT(*(res.wp.lock()), Eq(*(data.wp.lock())));
}
class PolymorphicNDCBase {
public:
virtual ~PolymorphicNDCBase() = 0;
template <typename S>
void serialize(S& ) {}
};
PolymorphicNDCBase::~PolymorphicNDCBase() = default;
class PolymorphicNDC1:public PolymorphicNDCBase {
int8_t i{};
friend class bitsery::Access;
template <typename S>
void serialize(S& s) {
s.value1b(i);
}
public:
PolymorphicNDC1() = default;
PolymorphicNDC1(int8_t v):i{v} {}
bool operator == (const PolymorphicNDC1& other) const {
return i == other.i;
}
};
class PolymorphicNDC2:public PolymorphicNDCBase {
uint16_t ui{};
friend class bitsery::Access;
template <typename S>
void serialize(S& s) {
s.value2b(ui);
}
public:
PolymorphicNDC2() = default;
PolymorphicNDC2(uint16_t v):ui{v} {}
bool operator == (const PolymorphicNDC2& other) const {
return ui == other.ui;
}
};
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<PolymorphicNDCBase> : PolymorphicDerivedClasses<PolymorphicNDC1, PolymorphicNDC2> {
};
}
}
struct PolymorphicPointers {
PolymorphicNDCBase* pp;
std::unique_ptr<PolymorphicNDCBase> up;
std::shared_ptr<PolymorphicNDCBase> sp;
std::weak_ptr<PolymorphicNDCBase> wp;
};
template <typename S>
void serialize(S& s, PolymorphicPointers& o) {
s.ext(o.pp, bitsery::ext::PointerOwner{});
s.ext(o.up, bitsery::ext::StdSmartPtr{});
s.ext(o.sp, bitsery::ext::StdSmartPtr{});
s.ext(o.wp, bitsery::ext::StdSmartPtr{});
}
TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
using TContext = std::tuple<bitsery::ext::PointerLinkingContext, bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
SerContext ctx{};
PolymorphicPointers data{};
data.pp = new PolymorphicNDC1{-4};
data.up = std::unique_ptr<PolymorphicNDCBase>(new PolymorphicNDC2{54});
data.sp = std::shared_ptr<PolymorphicNDCBase>(new PolymorphicNDC1{15});
data.wp = data.sp;
PolymorphicPointers res{};
TContext serCtx{};
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
TContext desCtx{};
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
ctx.createSerializer(&serCtx).object(data);
ctx.createDeserializer(&desCtx).object(res);
auto respp = dynamic_cast<PolymorphicNDC1*>(res.pp);
auto resup = dynamic_cast<PolymorphicNDC2*>(res.up.get());
auto ressp = dynamic_cast<PolymorphicNDC1*>(res.sp.get());
auto reswp = dynamic_cast<PolymorphicNDC1*>(res.wp.lock().get());
auto datapp = dynamic_cast<PolymorphicNDC1*>(data.pp);
auto dataup = dynamic_cast<PolymorphicNDC2*>(data.up.get());
auto datasp = dynamic_cast<PolymorphicNDC1*>(data.sp.get());
auto datawp = dynamic_cast<PolymorphicNDC1*>(data.wp.lock().get());
EXPECT_THAT(respp, ::testing::Ne(nullptr));
EXPECT_THAT(resup, ::testing::Ne(nullptr));
EXPECT_THAT(ressp, ::testing::Ne(nullptr));
EXPECT_THAT(reswp, ::testing::Ne(nullptr));
EXPECT_THAT(*respp, Eq(*datapp));
delete res.pp;
delete data.pp;
EXPECT_THAT(*resup, Eq(*dataup));
EXPECT_THAT(*ressp, Eq(*datasp));
EXPECT_THAT(*reswp, Eq(*datawp));
}

View File

@@ -22,16 +22,14 @@
#include <gmock/gmock.h>
#include <algorithm>
#include <numeric>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <bitsery/traits/list.h>
#include <bitsery/traits/deque.h>
#include <bitsery/traits/forward_list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::ContainerEq;
using testing::Eq;
@@ -266,4 +264,6 @@ TEST_P(SerializeContainer, SizeHasVariableLength) {
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
}
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000));
//last comma is to suppress error that otherwise can be suppressed by clang/gcc with -Wgnu-zero-variadic-macro-arguments
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000),);

View File

@@ -0,0 +1,240 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/compact_value.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <bitset>
#include <chrono>
using testing::Eq;
using bitsery::ext::CompactValue;
using bitsery::ext::CompactValueAsObject;
using bitsery::EndiannessType;
// helper function, that gets value filled with specified number of bits
template <typename TValue>
TValue getValue(bool isPositive, size_t significantBits) {
TValue v = isPositive ? 0 : -1;
if (significantBits == 0)
return v;
using TUnsigned = typename std::make_unsigned<TValue>::type;
TUnsigned mask = {};
mask = ~mask; // invert shiftByBits
auto shiftBy = bitsery::details::BitsSize<TValue>::value - significantBits;
mask >>= shiftBy;
//cast to unsigned when applying mask
return (TUnsigned)v ^ mask;
}
// helper function, that serialize and return deserialized value
template <typename TSerContext, typename TValue>
std::pair<TValue, size_t> serializeAndGetDeserialized(TValue data) {
TSerContext ctx;
TValue res{};
ctx.createSerializer().template ext<sizeof(TValue)>(data, CompactValue{});
ctx.createDeserializer().template ext<sizeof(TValue)>(res, CompactValue{});
return {res, ctx.getBufferSize()};
}
struct LittleEndianConfig: public bitsery::DefaultConfig {
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
};
struct BigEndianConfig: public bitsery::DefaultConfig {
static constexpr EndiannessType NetworkEndianness = EndiannessType::BigEndian;
};
template <typename TValue, bool isPositiveNr, typename TConfig>
struct TC {
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
using Value = TValue;
using Config = TConfig;
bool isPositive = isPositiveNr;
};
template<typename T>
class SerializeExtensionCompactValueCorrectness : public testing::Test {
public:
using TestCase = T;
};
using AllValueSizesTestCases = ::testing::Types<
TC<uint8_t, true, LittleEndianConfig>,
TC<uint16_t, true, LittleEndianConfig>,
TC<uint32_t, true, LittleEndianConfig>,
TC<uint64_t, true, LittleEndianConfig>,
TC<int8_t, true, LittleEndianConfig>,
TC<int16_t, true, LittleEndianConfig>,
TC<int32_t, true, LittleEndianConfig>,
TC<int64_t, true, LittleEndianConfig>,
TC<int8_t, false, LittleEndianConfig>,
TC<int16_t, false, LittleEndianConfig>,
TC<int32_t, false, LittleEndianConfig>,
TC<int64_t, false, LittleEndianConfig>,
TC<uint8_t, true, BigEndianConfig>,
TC<uint16_t, true, BigEndianConfig>,
TC<uint32_t, true, BigEndianConfig>,
TC<uint64_t, true, BigEndianConfig>,
TC<int8_t, true, BigEndianConfig>,
TC<int16_t, true, BigEndianConfig>,
TC<int32_t, true, BigEndianConfig>,
TC<int64_t, true, BigEndianConfig>,
TC<int8_t, false, BigEndianConfig>,
TC<int16_t, false, BigEndianConfig>,
TC<int32_t, false, BigEndianConfig>,
TC<int64_t, false, BigEndianConfig>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases);
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
for (auto i = 0u; i < bitsery::details::BitsSize<TValue>::value + 1; ++i) {
auto data = getValue<TValue>(tc.isPositive, i);
auto res = serializeAndGetDeserialized<BasicSerializationContext<typename TCase::Config, void>>(data);
EXPECT_THAT(res.first, Eq(data));
}
}
// this stucture will contain test data and result, as type paramters
template <typename TValue, bool isPositiveNr, size_t significantBits, size_t resultBytes>
struct SizeTC {
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
static_assert(bitsery::details::BitsSize<TValue>::value >= significantBits, "");
using Value = TValue;
bool isPositive = isPositiveNr;
size_t fillBits = significantBits;
size_t bytesCount = resultBytes;
};
template<typename T>
class SerializeExtensionCompactValueRequiredBytes : public testing::Test {
public:
using TestCase = T;
};
using RequiredBytesTestCases = ::testing::Types<
//1 byte always writes to 1 byte
SizeTC<uint8_t, true, 0,1>,
SizeTC<uint8_t, true, 8,1>,
SizeTC<int8_t, false, 0,1>,
SizeTC<int8_t, true, 8,1>,
//2 byte, +1 byte after 15 significant bits
SizeTC<uint16_t, true, 7,1>,
SizeTC<uint16_t, true, 8,2>,
SizeTC<uint16_t, true, 14,2>,
SizeTC<uint16_t, true, 15,3>,
//2 byte, +1 byte after 15-1 significant bits (1 bit for sign)
SizeTC<int16_t, true, 6,1>,
SizeTC<int16_t, false, 7,2>,
SizeTC<int16_t, true, 13,2>,
SizeTC<int16_t, false, 14,3>,
//4 byte, +1 byte after 29 significant bits
SizeTC<uint32_t, true, 14,2>,
SizeTC<uint32_t, true, 21,3>,
SizeTC<uint32_t, true, 28,4>,
SizeTC<uint32_t, true, 29,5>,
SizeTC<uint32_t, true, 32,5>,
//4 byte
SizeTC<int32_t, true, 13,2>,
SizeTC<int32_t, false, 20,3>,
SizeTC<int32_t, true, 27,4>,
SizeTC<int32_t, false, 28,5>,
SizeTC<int32_t, true, 31,5>,
//8 byte, +1 byte after 57 significant bits, or +2 byte when all bits are significant
SizeTC<uint64_t, true, 28,4>,
SizeTC<uint64_t, true, 35,5>,
SizeTC<uint64_t, true, 42,6>,
SizeTC<uint64_t, true, 49,7>,
SizeTC<uint64_t, true, 56,8>,
SizeTC<uint64_t, true, 57,9>,
SizeTC<uint64_t, true, 63,9>,
SizeTC<uint64_t, true, 64,10>,
//8 byte,
SizeTC<int64_t, true, 27,4>,
SizeTC<int64_t, false, 34,5>,
SizeTC<int64_t, true, 41,6>,
SizeTC<int64_t, false, 48,7>,
SizeTC<int64_t, true, 55,8>,
SizeTC<int64_t, false, 56,9>,
SizeTC<int64_t, true, 62,9>,
SizeTC<int64_t, false, 63,10>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases);
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
TValue data = getValue<TValue>(tc.isPositive, tc.fillBits);
auto res = serializeAndGetDeserialized<SerializationContext>(data);
EXPECT_THAT(res.first, Eq(data));
EXPECT_THAT(res.second, tc.bytesCount);
}
enum b1En: uint8_t {
A,B,C,D=54,E
};
enum class b8En: int64_t {
A=-874987489,B,C=0,D,E=489748978, F,G
};
TEST(SerializeExtensionCompactValueEnum, TestEnums) {
auto d1 = b1En::E;
auto d2 = b8En::B;
auto d3 = b8En::F;
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d1).first, Eq(d1));
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d2).first, Eq(d2));
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d3).first, Eq(d3));
}
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
SerializationContext ctx;
auto data = getValue<uint32_t >(true, 17);
uint16_t res{};
auto& ser = ctx.createSerializer();
ser.ext(data, CompactValueAsObject{});
auto& des = ctx.createDeserializer();
des.ext(res, CompactValueAsObject{});
auto& rd = bitsery::AdapterAccess::getReader(des);
EXPECT_THAT(data, ::testing::Ne(res));
EXPECT_THAT(rd.error(), Eq(bitsery::ReaderError::DataOverflow));
}

View File

@@ -21,11 +21,12 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/entropy.h>
#include <bitsery/traits/list.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using namespace testing;
using bitsery::ext::Entropy;

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/growable.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/growable.h>
using namespace testing;
@@ -52,9 +52,9 @@ TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
ser.value1b(v);
});
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(ctx.getBufferSize(), Eq(1u));
ctx.bw->flush();
EXPECT_THAT(ctx.getBufferSize(), Gt(1));
EXPECT_THAT(ctx.getBufferSize(), Gt(1u));
}

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/inheritance.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;

View File

@@ -20,9 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
@@ -34,34 +34,33 @@ using testing::Eq;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, PointerLinkingContext>;
class SerializeExtensionPointerSerialization: public testing::Test {
class SerializeExtensionPointerSerialization : public testing::Test {
public:
//data used for serialization
int16_t d1{1597};
int16_t* pd1 = &d1;
int16_t *pd1 = &d1;
MyEnumClass d2{MyEnumClass::E2};
MyEnumClass* pd2 = &d2;
MyEnumClass *pd2 = &d2;
MyStruct1 d3{184, 897};
MyStruct1* pd3 = &d3;
MyStruct1 *pd3 = &d3;
//data used for deserialization
int16_t r1{-84};
int16_t* pr1 = &r1;
int16_t *pr1 = &r1;
MyEnumClass r2{MyEnumClass::E4};
MyEnumClass* pr2 = &r2;
MyEnumClass *pr2 = &r2;
MyStruct1 r3{-4984, -14597};
MyStruct1* pr3 = &r3;
MyStruct1 *pr3 = &r3;
//null pointers
int16_t* p1null = nullptr;
MyEnumClass* p2null = nullptr;
MyStruct1* p3null = nullptr;
int16_t *p1null = nullptr;
MyEnumClass *p2null = nullptr;
MyStruct1 *p3null = nullptr;
PointerLinkingContext plctx1{};
SerContext sctx1{};
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer &createSerializer() {
return sctx1.createSerializer(&plctx1);
}
@@ -71,7 +70,7 @@ public:
};
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
MyStruct1* data = nullptr;
MyStruct1 *data = nullptr;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
@@ -89,17 +88,34 @@ TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
MyStruct1 data{};
//pretend that this is shared ptr
MyStruct1* sharedPtr = &data;
MyStruct1 *sharedPtr = &data;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
}
TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextIsInvalid) {
MyStruct1 data1{};
MyStruct1 data2{};
//pretend that this is shared ptr
MyStruct1 *sharedPtr1 = &data1;
MyStruct1 *sharedPtr2 = &data2;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(2));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_FALSE(plctx1.isValid());
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(2));
EXPECT_TRUE(plctx1.isValid());
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
auto& ser = createSerializer();
auto &ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(p1null, PointerObserver{});
ser.ext(p3null, PointerOwner{});
@@ -109,11 +125,12 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
#ifndef NDEBUG
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNullThenAssert) {
MyStruct1 tmp;
MyStruct1* data = &tmp;
MyStruct1 *data = &tmp;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
@@ -122,7 +139,7 @@ TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNu
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
auto& ser = createSerializer();
auto &ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
@@ -132,7 +149,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAs
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(d3, ReferencedByPointer{});
@@ -140,14 +157,14 @@ TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPo
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext4b(d2, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAssert) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
EXPECT_DEATH(ser1.ext2b(p1null, PointerOwner{PointerType::NotNull}), "");
EXPECT_DEATH(ser1.ext2b(p1null, PointerObserver{PointerType::NotNull}), "");
}
@@ -155,7 +172,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAsser
#endif
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
@@ -168,7 +185,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerT
}
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
@@ -180,7 +197,7 @@ TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedB
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext(p3null, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
sctx1.createDeserializer();
@@ -193,7 +210,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZ
}
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
@@ -212,7 +229,7 @@ TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
}
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
@@ -221,12 +238,12 @@ TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeOb
}
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext2b(d1, ReferencedByPointer{});
ser1.ext4b(d2, ReferencedByPointer{});
ser1.ext4b(pd2, PointerObserver{});
auto& des = sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3+6));
auto &des = sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3 + 6));
size_t id{};
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(1));
@@ -241,12 +258,12 @@ TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAn
}
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
auto& ser1 = createSerializer();
auto &ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(pd3, PointerOwner{});
auto& des1 = sctx1.createDeserializer();
auto &des1 = sctx1.createDeserializer();
//2x ids + int32_t + MyStruct1
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE ));
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
size_t id;
bitsery::details::readSize(*sctx1.br, id, 10000u);
des1.value4b(r2);
@@ -256,25 +273,25 @@ TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject
EXPECT_THAT(r3, Eq(*pd3));
}
class SerializeExtensionPointerDeserialization: public SerializeExtensionPointerSerialization {
class SerializeExtensionPointerDeserialization : public SerializeExtensionPointerSerialization {
public:
typename SerContext::TSerializer& createSerializer() {
typename SerContext::TSerializer &createSerializer() {
return sctx1.createSerializer(&plctx1);
}
typename SerContext::TDeserializer& createDeserializer() {
typename SerContext::TDeserializer &createDeserializer() {
return sctx1.createDeserializer(&plctx1);
}
};
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
auto& ser = createSerializer();
auto &ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext4b(d2, ReferencedByPointer{});
ser.ext(d3, ReferencedByPointer{});
auto& des = createDeserializer();
auto &des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
des.ext4b(r2, ReferencedByPointer{});
des.ext(r3, ReferencedByPointer{});
@@ -285,10 +302,10 @@ TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
}
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNullPointerThenInvalidPointerError) {
auto& ser = createSerializer();
auto &ser = createSerializer();
bitsery::details::writeSize(*sctx1.bw, 0u);
ser.ext2b(d1, ReferencedByPointer{});
auto& des = createDeserializer();
auto &des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}
@@ -296,21 +313,21 @@ TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNul
TEST_F(SerializeExtensionPointerDeserialization, WhenNonNullPointerIsNullThenInvalidPointerError) {
createSerializer();
bitsery::details::writeSize(*sctx1.bw, 0u);
auto& des1 = createDeserializer();
auto &des1 = createDeserializer();
des1.ext2b(p1null, PointerOwner{PointerType::NotNull});
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
auto& des2 = createDeserializer();
auto &des2 = createDeserializer();
des2.ext2b(p1null, PointerObserver{PointerType::NotNull});
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
auto& ser = createSerializer();
auto &ser = createSerializer();
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
auto &des = createDeserializer();
des.ext2b(p1null, PointerOwner{});
des.ext4b(p2null, PointerOwner{});
des.ext(p3null, PointerOwner{});
@@ -325,15 +342,15 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
auto& ser = createSerializer();
auto &ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext4b(p2null, PointerOwner{});
ser.ext(p3null, PointerOwner{});
auto& des = createDeserializer();
auto &des = createDeserializer();
//pr cannot link to local variables, need to allocate them separately
pr1 = new int16_t{};
pr2 = new MyEnumClass{};
pr3 = new MyStruct1{3,4};
pr3 = new MyStruct1{3, 4};
des.ext2b(pr1, PointerOwner{});
des.ext4b(pr2, PointerOwner{});
des.ext(pr3, PointerOwner{});
@@ -345,7 +362,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
}
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
auto& ser = createSerializer();
auto &ser = createSerializer();
//first owner, than observer
ser.ext4b(d2, ReferencedByPointer{});
ser.ext2b(p1null, PointerObserver{});
@@ -353,7 +370,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
//first observer, than owner
ser.ext(pd3, PointerObserver{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
auto &des = createDeserializer();
des.ext4b(r2, ReferencedByPointer{});
des.ext2b(pr1, PointerObserver{});
des.ext4b(p2null, PointerObserver{});
@@ -371,18 +388,18 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
EXPECT_THAT(pr3, Eq(&r3));
}
struct Test1Data {
std::vector<MyStruct1> vdata;
std::vector<MyStruct1*> vptr;
std::vector<MyStruct1 *> vptr;
MyStruct1 o1;
MyStruct1* po1;
MyStruct1 *po1;
int32_t i1;
int32_t* pi1;
template <typename S>
void serialize(S& s) {
int32_t *pi1;
template<typename S>
void serialize(S &s) {
//set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [&s](MyStruct1& d){
s.container(vdata, 100, [&s](MyStruct1 &d) {
s.ext(d, ReferencedByPointer{});
});
//contains non owning pointers
@@ -390,7 +407,7 @@ struct Test1Data {
//IMPORTANT !!!
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
//
s.container(vptr, 100, [&s](MyStruct1* (&d)){
s.container(vptr, 100, [&s](MyStruct1 *(&d)) {
s.ext(d, PointerObserver{});
});
//just a regular fields
@@ -407,15 +424,15 @@ struct Test1Data {
TEST(SerializeExtensionPointer, IntegrationTest) {
Test1Data data{};
data.vdata.push_back({165,-45});
data.vdata.push_back({7895,-1576});
data.vdata.push_back({5987,-798});
data.vdata.push_back({165, -45});
data.vdata.push_back({7895, -1576});
data.vdata.push_back({5987, -798});
//container of non owning pointers (observers)
data.vptr.push_back(nullptr);
data.vptr.push_back(std::addressof(data.vdata[0]));
data.vptr.push_back(std::addressof(data.vdata[2]));
//regular fields
data.o1 = MyStruct1{145,948};
data.o1 = MyStruct1{145, 948};
data.i1 = 945415;
//observer
data.po1 = std::addressof(data.vdata[1]);
@@ -447,4 +464,80 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
//free owning raw pointers
delete data.pi1;
delete res.pi1;
}
}
TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload) {
const int32_t NEW_VALUE = 2;
const int32_t OLD_VALUE = 1;
MyStruct1 *data = new MyStruct1{NEW_VALUE, NEW_VALUE};
MyStruct1 *res = new MyStruct1{OLD_VALUE, OLD_VALUE};
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
auto &ser = sctx1.createSerializer(&plctx1);
ser.ext(data, PointerOwner{}, [&ser](MyStruct1 &o) {
//serialize only one field
ser.value4b(o.i1);
});
auto &des = sctx1.createDeserializer(&plctx1);
des.ext(res, PointerOwner{}, [&des](MyStruct1 &o) {
//deserialize only one field
des.value4b(o.i1);
});
EXPECT_THAT(res->i1, Eq(NEW_VALUE));
EXPECT_THAT(res->i2, Eq(OLD_VALUE));//we didn't serialized that
delete data;
delete res;
}
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
const int32_t NEW_VALUE = 2;
const int32_t OLD_VALUE = 1;
MyStruct1 data = MyStruct1{NEW_VALUE, NEW_VALUE};
MyStruct1 res = MyStruct1{OLD_VALUE, OLD_VALUE};
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
auto &ser = sctx1.createSerializer(&plctx1);
ser.ext(data, ReferencedByPointer{}, [&ser](MyStruct1 &o) {
//serialize only one field
ser.value4b(o.i1);
});
auto &des = sctx1.createDeserializer(&plctx1);
des.ext(res, ReferencedByPointer{}, [&des](MyStruct1 &o) {
//deserialize only one field
des.value4b(o.i1);
});
EXPECT_THAT(res.i1, Eq(NEW_VALUE));
EXPECT_THAT(res.i2, Eq(OLD_VALUE));//we didn't serialized that
}
TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
auto *data = new int64_t{49845894};
auto *res = new int64_t{-78548415};
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).ext8b(data, PointerOwner{});
sctx1.createDeserializer(&plctx1).ext8b(res, PointerOwner{});
EXPECT_THAT(*res, Eq(*data));
delete data;
delete res;
}
TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload) {
int64_t data{49845894};
int64_t res{-78548415};
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).ext8b(data, ReferencedByPointer{});
sctx1.createDeserializer(&plctx1).ext8b(res, ReferencedByPointer{});
EXPECT_THAT(res, Eq(data));
}

View File

@@ -20,59 +20,71 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
using bitsery::ext::InheritanceContext;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PointerType;
using testing::Eq;
using TContext = std::tuple<PointerLinkingContext, bitsery::ext::InheritanceContext>;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
/*
* base class
*/
struct Base {
uint8_t x{};
virtual ~Base() = default;
};
template <typename S>
void serialize(S& s, Base& o) {
template<typename S>
void serialize(S &s, Base &o) {
s.value1b(o.x);
}
struct Derived1:virtual Base {
struct Derived1 : virtual Base {
uint8_t y1{};
};
template <typename S>
void serialize(S& s, Derived1& o) {
template<typename S>
void serialize(S &s, Derived1 &o) {
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y1);
}
struct Derived2:virtual Base {
struct Derived2 : virtual Base {
uint8_t y2{};
};
template <typename S>
void serialize(S& s, Derived2& o) {
template<typename S>
void serialize(S &s, Derived2 &o) {
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y2);
}
struct MultipleVirtualInheritance: Derived1, Derived2 {
struct MultipleVirtualInheritance : Derived1, Derived2 {
int8_t z{};
MultipleVirtualInheritance() = default;
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, uint8_t z_) {
@@ -82,8 +94,8 @@ struct MultipleVirtualInheritance: Derived1, Derived2 {
z = z_;
}
template <typename S>
void serialize(S& s) {
template<typename S>
void serialize(S &s) {
s.ext(*this, BaseClass<Derived1>{});
s.ext(*this, BaseClass<Derived2>{});
s.value1b(z);
@@ -91,40 +103,69 @@ struct MultipleVirtualInheritance: Derived1, Derived2 {
};
//define PolymorphicBase relationships for runtime polymorphism
//this class has no relationships specified via PolymorphicBaseClass
struct NoRelationshipSpecifiedDerived : Base {
};
//these classes will be used to "cheat" a little bit when testing deserialization flows
struct BaseClone {
uint8_t x{};
virtual ~BaseClone() = default;
};
template<typename S>
void serialize(S &s, BaseClone &o) {
s.value1b(o.x);
}
//define relationships between base class and derived classes for runtime polymorphism
namespace bitsery {
namespace ext {
template <>
struct PolymorphicBaseClass<Base>: DerivedClasses<Derived1, Derived2> {};
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived1, Derived2> {
};
template <>
struct PolymorphicBaseClass<Derived1>: DerivedClasses<MultipleVirtualInheritance> {};
// this is commented on purpose, to test scenario when base class is registered (Base)
// but using instance of Derived1 which is not registered as base
// template<>
// struct PolymorphicBaseClass<Derived1> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
// };
template <>
struct PolymorphicBaseClass<Derived2>: DerivedClasses<MultipleVirtualInheritance> {};
template<>
struct PolymorphicBaseClass<Derived2> : PolymorphicDerivedClasses<MultipleVirtualInheritance> {
};
}
}
class SerializeExtensionPointerPolymorphicTypes: public testing::Test {
class SerializeExtensionPointerPolymorphicTypes : public testing::Test {
public:
TContext plctx1{};
SerContext sctx1{};
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer& createSerializer() {
return sctx1.createSerializer(&plctx1);
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer& createDeserializer() {
return sctx1.createDeserializer(&plctx1);
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx1).isValid();
return std::get<0>(plctx).isValid();
}
virtual void TearDown() override {
@@ -133,9 +174,9 @@ public:
};
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
Base* baseData = nullptr;
Base *baseData = nullptr;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = nullptr;
Base *baseRes = nullptr;
createDeserializer().ext(baseRes, PointerOwner{});
EXPECT_THAT(baseRes, ::testing::IsNull());
@@ -143,10 +184,10 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result0) {
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, Data0Result1) {
Base* baseData = nullptr;
Base *baseData = nullptr;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = new Derived1{};
Base *baseRes = new Derived1{};
createDeserializer().ext(baseRes, PointerOwner{});
EXPECT_THAT(baseRes, ::testing::IsNull());
@@ -157,13 +198,13 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result0) {
Derived1 d1{};
d1.x = 3;
d1.y1 = 78;
Base* baseData = &d1;
Base *baseData = &d1;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = nullptr;
Base *baseRes = nullptr;
createDeserializer().ext(baseRes, PointerOwner{});
auto* data = dynamic_cast<Derived1*>(baseData);
auto* res = dynamic_cast<Derived1*>(baseRes);
auto *data = dynamic_cast<Derived1 *>(baseData);
auto *res = dynamic_cast<Derived1 *>(baseRes);
EXPECT_THAT(baseRes, ::testing::NotNull());
EXPECT_THAT(data, ::testing::NotNull());
@@ -177,13 +218,13 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
Derived1 d1{};
d1.x = 3;
d1.y1 = 78;
Base* baseData = &d1;
Base *baseData = &d1;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = &d1;
Base *baseRes = &d1;
createDeserializer().ext(baseRes, PointerOwner{});
auto* data = dynamic_cast<Derived1*>(baseData);
auto* res = dynamic_cast<Derived1*>(baseRes);
auto *data = dynamic_cast<Derived1 *>(baseData);
auto *res = dynamic_cast<Derived1 *>(baseRes);
EXPECT_THAT(baseRes, ::testing::NotNull());
EXPECT_THAT(data, ::testing::NotNull());
@@ -192,19 +233,19 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, Data1Result1) {
EXPECT_THAT(res->y1, Eq(data->y1));
}
TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeData1Result0) {
TEST_F(SerializeExtensionPointerPolymorphicTypes, ComplexTypeWithVirtualInheritanceData1Result0) {
MultipleVirtualInheritance md1{};
md1.x = 3;
md1.y1 = 78;
md1.y2 = 14;
md1.z = -33;
Base* baseData = &md1;
Base *baseData = &md1;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = nullptr;
Base *baseRes = nullptr;
createDeserializer().ext(baseRes, PointerOwner{});
auto* data = dynamic_cast<MultipleVirtualInheritance*>(baseData);
auto* res = dynamic_cast<MultipleVirtualInheritance*>(baseRes);
auto *data = dynamic_cast<MultipleVirtualInheritance *>(baseData);
auto *res = dynamic_cast<MultipleVirtualInheritance *>(baseRes);
EXPECT_THAT(baseRes, ::testing::NotNull());
EXPECT_THAT(data, ::testing::NotNull());
@@ -222,28 +263,66 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes, WhenResultIsDifferentTypeThenR
md1.y1 = 78;
md1.y2 = 14;
md1.z = -33;
Base* baseData = &md1;
Base *baseData = &md1;
createSerializer().ext(baseData, PointerOwner{});
Base* baseRes = new Derived1{};
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes), ::testing::IsNull());
Base *baseRes = new Derived1{};
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::IsNull());
createDeserializer().ext(baseRes, PointerOwner{});
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance*>(baseRes), ::testing::NotNull());
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(baseRes), ::testing::NotNull());
delete baseRes;
}
//struct UnknownType:Base {
//};
//
//template <typename S>
//void serialize(S& s, UnknownType& o) {
// s.ext(o, VirtualBaseClass<Base>{});
//}
#ifndef NDEBUG
// todo reimplement whole polymorphism thing, because with current solution this test fails
//TEST(SerializeExtensionPointerPolymorphicTypesErrors, WhenSerializingUnknownTypeThenAssert) {
// TContext plctx1{};
// SerContext sctx1{};
// UnknownType obj{};
// UnknownType* unknownPtr = &obj;
// EXPECT_DEATH(sctx1.createSerializer(&plctx1).ext(unknownPtr, PointerOwner{}), "");
//}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenSerializingDerivedTypeWithoutSpecifiedRelationshipsWithBaseThenAssert) {
NoRelationshipSpecifiedDerived md1;//this class has no relationships specified via PolymorphicBaseClass
Base *baseData = &md1;
EXPECT_DEATH(createSerializer().ext(baseData, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenDeserializingDerivedTypeNotRegisteredWithPolymorphicContextThenAssert) {
Derived1 d1{};
Base *baseData = &d1;
createSerializer().ext(baseData, PointerOwner{});
BaseClone *baseRes = nullptr; //this class is not registered
EXPECT_DEATH(createDeserializer().ext(baseRes, PointerOwner{}), "");
}
#endif
TEST_F(SerializeExtensionPointerPolymorphicTypes,
CompileTimeTypeIsDerivedAndReachableFromBaseRegisteredWithPolymorphicContext) {
MultipleVirtualInheritance md;
Derived2 *derivedData = &md;//this class is not registered via PolymorphicContext
createSerializer().ext(derivedData, PointerOwner{});
Derived2 *derivedRes = new Derived2{};
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::IsNull());
createDeserializer().ext(derivedRes, PointerOwner{});
EXPECT_THAT(dynamic_cast<MultipleVirtualInheritance *>(derivedRes), ::testing::NotNull());
delete derivedRes;
}
TEST_F(SerializeExtensionPointerPolymorphicTypes,
WhenPolymorphicTypeNotFoundDuringDeserializionThenInvalidPointerError) {
Derived1 d1{};
Base *baseData = &d1;
createSerializer().ext(baseData, PointerOwner{});
BaseClone *baseRes = nullptr; //this class will be registered, but it doesn't have relationships specified via PolymorphicBaseClass
auto &des = sctx.createDeserializer(&plctx);
auto &pc = std::get<2>(plctx);
pc.clear();
pc.registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<BaseClone>{});
des.ext(baseRes, PointerOwner{});
EXPECT_THAT(sctx.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}

View File

@@ -0,0 +1,81 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_chrono.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdDuration = bitsery::ext::StdDuration;
using StdTimePoint = bitsery::ext::StdTimePoint;
using testing::Eq;
TEST(SerializeExtensionStdChrono, IntegralDuration) {
SerializationContext ctx1;
using Hours = std::chrono::duration<int32_t, std::ratio<60>>;
Hours data{43};
Hours res{};
ctx1.createSerializer().ext4b(data, StdDuration{});
ctx1.createDeserializer().ext4b(res, StdDuration{});
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, IntegralTimePoint) {
SerializationContext ctx1;
using Duration = std::chrono::duration<int64_t, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;
TP data{Duration{243}};
TP res{};
ctx1.createSerializer().ext8b(data, StdTimePoint{});
ctx1.createDeserializer().ext8b(res, StdTimePoint{});
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, FloatDuration) {
SerializationContext ctx1;
using Hours = std::chrono::duration<float, std::ratio<60>>;
Hours data{43.5f};
Hours res{};
ctx1.createSerializer().ext4b(data, StdDuration{});
ctx1.createDeserializer().ext4b(res, StdDuration{});
EXPECT_THAT(res, Eq(data));
}
TEST(SerializeExtensionStdChrono, FloatTimePoint) {
SerializationContext ctx1;
using Duration = std::chrono::duration<double, std::milli>;
using TP = std::chrono::time_point<std::chrono::system_clock, Duration>;
TP data{Duration{243457.4}};
TP res{};
ctx1.createSerializer().ext8b(data, StdTimePoint{});
ctx1.createDeserializer().ext8b(res, StdTimePoint{});
EXPECT_THAT(res, Eq(data));
}

View File

@@ -20,14 +20,14 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_map.h>
#include <bitsery/ext/entropy.h>
#include <unordered_map>
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdMap = bitsery::ext::StdMap;
using testing::Eq;

View File

@@ -26,17 +26,15 @@
#if __cplusplus > 201402L
#include <optional>
#include <bitsery/ext/std_optional.h>
#include <bitsery/ext/value_range.h>
using StdOptional = bitsery::ext::StdOptional;
using testing::Eq;
using BPSer = SerializationContext::TSerializer::BPEnabledType;
using BPDes = SerializationContext::TDeserializer::BPEnabledType;
using BPSer = bitsery::BasicSerializer<Writer, true>;
using BPDes = bitsery::BasicDeserializer<Reader, true>;
using testing::Eq;
template <typename T>
void test(SerializationContext& ctx, const T& v, T& r) {
@@ -117,12 +115,15 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
});
});
EXPECT_THAT(range.getRequiredBits() + 1, ::testing::Lt(8));
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(t1.value(), Eq(r1.value()));
}
#else
#if defined(_MSC_VER)
#pragma message("Tests for StdOptional requires C++17")
#else
#warning "Tests for StdOptional requires C++17"
#endif
#endif

View File

@@ -20,9 +20,10 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_queue.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_queue.h>
using StdQueue = bitsery::ext::StdQueue;

View File

@@ -20,12 +20,12 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_set.h>
#include <set>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using StdSet = bitsery::ext::StdSet;
using testing::Eq;
@@ -35,7 +35,7 @@ class SerializeExtensionStdSet : public testing::Test {
public:
using TContainer = T;
const TContainer src = {4, 8, 48, 4, 9845, 64, 8};
TContainer res{};
TContainer res{78,74,154,8};
};
using SerializeExtensionStdSetTypes = ::testing::Types<
@@ -65,7 +65,7 @@ TEST(SerializeExtensionStdSet, ObjectSyntax) {
TEST(SerializeExtensionStdSet, FunctionSyntax) {
SerializationContext ctx1;
std::unordered_multiset<int32_t> t1{54,-484,841,79};
std::unordered_multiset<int32_t> r1{};
std::unordered_multiset<int32_t> r1{74,878,15,16,-7,5,-4,8,7};
auto& ser = ctx1.createSerializer();
ser.ext(t1, StdSet{10}, [&ser](int32_t& v) {
ser.value4b(v);
@@ -75,4 +75,4 @@ TEST(SerializeExtensionStdSet, FunctionSyntax) {
des.value4b(v);
});
EXPECT_THAT(r1, Eq(t1));
}
}

View File

@@ -0,0 +1,690 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
using bitsery::ext::InheritanceContext;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerType;
using bitsery::ext::StdSmartPtr;
using bitsery::ext::PointerObserver;
using testing::Eq;
using testing::Ne;
struct Base {
uint8_t x{};
virtual ~Base() = default;
};
template<typename S>
void serialize(S &s, Base &o) {
s.value1b(o.x);
}
struct Derived : virtual Base {
uint8_t y{};
Derived() = default;
Derived(uint8_t x_, uint8_t y_) {
x = x_;
y = y_;
}
};
template<typename S>
void serialize(S &s, Derived &o) {
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y);
}
struct MoreDerived : Derived {
int8_t z{};
MoreDerived() = default;
MoreDerived(uint8_t x_, uint8_t y_, uint8_t z_) : Derived(x_, y_) {
z = z_;
}
};
template<typename S>
void serialize(S &s, MoreDerived &o) {
s.ext(o, BaseClass<Derived>{});
s.value1b(o.z);
}
//define relationships between base class and derived classes for runtime polymorphism
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Base> : PolymorphicDerivedClasses<Derived> {
};
template<>
struct PolymorphicBaseClass<Derived> : PolymorphicDerivedClasses<MoreDerived> {
};
}
}
template<typename T>
class SerializeExtensionStdSmartPtrNonPolymorphicType : public testing::Test {
public:
template<typename U>
using TPtr = typename T::template TData<U>;
using TExt = typename T::TExt;
using TContext = std::tuple<PointerLinkingContext>;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
};
template<typename T>
class SerializeExtensionStdSmartPtrPolymorphicType : public testing::Test {
public:
template<typename U>
using TPtr = typename T::template TData<U>;
using TExt = typename T::TExt;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
virtual void TearDown() override {
EXPECT_TRUE(isPointerContextValid());
}
};
struct UniquePtrTest {
template<typename T>
using TData = std::unique_ptr<T>;
using TExt = StdSmartPtr;
};
struct SharedPtrTest {
template<typename T>
using TData = std::shared_ptr<T>;
using TExt = StdSmartPtr;
};
using TestingWithNonPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes);
using TestingWithPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes);
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
Ptr data{};
this->createSerializer().ext(data, Ext{});
Ptr res{};
this->createDeserializer().ext(res, Ext{});
EXPECT_THAT(data.get(), ::testing::IsNull());
EXPECT_THAT(res.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result1) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
Ptr data{};
this->createSerializer().ext(data, Ext{});
Ptr res{new MyStruct1{}};
this->createDeserializer().ext(res, Ext{});
EXPECT_THAT(data.get(), ::testing::IsNull());
EXPECT_THAT(res.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result0) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
Ptr data{new MyStruct1{3, 78}};
this->createSerializer().ext(data, Ext{});
Ptr res{};
this->createDeserializer().ext(res, Ext{});
EXPECT_THAT(data.get(), ::testing::NotNull());
EXPECT_THAT(res.get(), ::testing::NotNull());
EXPECT_THAT(res->i1, Eq(data->i1));
EXPECT_THAT(res->i2, Eq(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data1Result1) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
Ptr data{new MyStruct1{3, 78}};
this->createSerializer().ext(data, Ext{});
Ptr res{new MyStruct1{}};
this->createDeserializer().ext(res, Ext{});
EXPECT_THAT(data.get(), ::testing::NotNull());
EXPECT_THAT(res.get(), ::testing::NotNull());
EXPECT_THAT(res->i1, Eq(data->i1));
EXPECT_THAT(res->i2, Eq(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
Ptr data{new MyStruct1{3, 78}};
auto &ser = this->createSerializer();
ser.ext(data, Ext{}, [&ser](MyStruct1 &o) {
//serialize only one field
ser.value4b(o.i1);
});
Ptr res{new MyStruct1{97, 12}};
auto &des = this->createDeserializer();
des.ext(res, Ext{}, [&des](MyStruct1 &o) {
des.value4b(o.i1);
});
EXPECT_THAT(res->i1, Eq(data->i1));
EXPECT_THAT(res->i2, Ne(data->i2));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseValueOverload) {
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
this->createSerializer().ext2b(data, Ext{});
Ptr res{};
this->createDeserializer().ext2b(res, Ext{});
EXPECT_THAT(*res, Eq(*data));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerObserver) {
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
ser.ext2b(data, Ext{});
ser.ext2b(dataObs, PointerObserver{});
Ptr res{};
uint16_t *resObs = nullptr;
auto &des = this->createDeserializer();
des.ext2b(res, Ext{});
des.ext2b(resObs, PointerObserver{});
EXPECT_THAT(resObs, Eq(res.get()));
}
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserverThenPtr) {
using Ptr = typename TestFixture::template TPtr<uint16_t>;
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
ser.ext2b(dataObs, PointerObserver{});
ser.ext2b(data, Ext{});
Ptr res{};
uint16_t *resObs = nullptr;
auto &des = this->createDeserializer();
des.ext2b(resObs, PointerObserver{});
des.ext2b(res, Ext{});
EXPECT_THAT(resObs, Eq(res.get()));
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result0) {
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
Ptr baseData{};
this->createSerializer().ext(baseData, Ext{});
Ptr baseRes{};
this->createDeserializer().ext(baseRes, Ext{});
EXPECT_THAT(baseRes.get(), ::testing::IsNull());
EXPECT_THAT(baseData.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data0Result1) {
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
Ptr baseData{};
this->createSerializer().ext(baseData, Ext{});
Ptr baseRes{new Derived{}};
this->createDeserializer().ext(baseRes, Ext{});
EXPECT_THAT(baseRes.get(), ::testing::IsNull());
EXPECT_THAT(baseData.get(), ::testing::IsNull());
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, Data1Result0) {
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
Ptr baseData{new Derived{3, 78}};
this->createSerializer().ext(baseData, Ext{});
Ptr baseRes{};
this->createDeserializer().ext(baseRes, Ext{});
auto *data = dynamic_cast<Derived *>(baseData.get());
auto *res = dynamic_cast<Derived *>(baseRes.get());
EXPECT_THAT(data, ::testing::NotNull());
EXPECT_THAT(res, ::testing::NotNull());
EXPECT_THAT(res->x, Eq(data->x));
EXPECT_THAT(res->y, Eq(data->y));
}
TYPED_TEST(SerializeExtensionStdSmartPtrPolymorphicType, DataAndResultWithDifferentRuntimeTypes) {
using Ptr = typename TestFixture::template TPtr<Base>;
using Ext = typename TestFixture::TExt;
Ptr baseData{new Derived{3, 78}};
this->createSerializer().ext(baseData, Ext{});
Ptr baseRes{new Base{}};
this->createDeserializer().ext(baseRes, Ext{});
auto *data = dynamic_cast<Derived *>(baseData.get());
auto *res = dynamic_cast<Derived *>(baseRes.get());
EXPECT_THAT(data, ::testing::NotNull());
EXPECT_THAT(res, ::testing::NotNull());
EXPECT_THAT(res->x, Eq(data->x));
EXPECT_THAT(res->y, Eq(data->y));
}
class SerializeExtensionStdSmartSharedPtr : public testing::Test {
public:
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
typename SerContext::TSerializer &createSerializer() {
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
typename SerContext::TDeserializer &createDeserializer() {
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
size_t getBufferSize() const {
return sctx.getBufferSize();
}
bool isPointerContextValid() {
return std::get<0>(plctx).isValid();
}
void clearSharedState() {
return std::get<0>(plctx).clearSharedState();
}
};
TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{baseData1};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
auto &des = createDeserializer();
//1b linking context (for 1st time)
//1b dynamic type info
//2b Derived object
//1b linking context (for 2nd time)
EXPECT_THAT(getBufferSize(), Eq(5));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearSharedState) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_THAT(baseRes1.use_count(), Eq(2));
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{new Derived{55, 11}};
std::shared_ptr<Base> baseData21{baseData2};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData21, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Base> baseRes2{};
std::shared_ptr<Base> baseRes21{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes21, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes2.use_count(), Eq(2));
EXPECT_THAT(baseRes21.use_count(), Eq(2));
baseRes2.reset();
EXPECT_THAT(baseRes21.use_count(), Eq(1));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::weak_ptr<Base> baseData11{baseData1};
std::weak_ptr<Base> baseData12{baseData11};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData12, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes12{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes12, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes11.use_count(), Eq(1));
EXPECT_THAT(baseRes12.use_count(), Eq(1));
baseRes1.reset();
EXPECT_THAT(baseRes11.use_count(), Eq(0));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::weak_ptr<Base> baseData11{baseData1};
std::weak_ptr<Base> baseData2{};
auto &ser = createSerializer();
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes2{};
auto &des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
EXPECT_THAT(baseRes11.use_count(), Eq(1));
baseRes1.reset();
EXPECT_THAT(baseRes11.use_count(), Eq(0));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{};
std::weak_ptr<Base> baseData3{};
std::weak_ptr<Base> baseData11{baseData1};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData3, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Base> baseRes2{new Derived{3, 78}};
std::weak_ptr<Base> baseRes3{baseRes2};
std::weak_ptr<Base> baseRes11{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes3, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes2.use_count(), Eq(0));
EXPECT_THAT(baseRes3.use_count(), Eq(0));
EXPECT_THAT(baseRes11.use_count(), Eq(1));
baseRes1.reset();
EXPECT_THAT(baseRes11.use_count(), Eq(0));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Base{}};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
EXPECT_THAT(dynamic_cast<Derived *>(baseRes1.get()), ::testing::NotNull());
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid) {
std::shared_ptr<Base> tmp{new Derived{3, 78}};
std::weak_ptr<Base> baseData1{tmp};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
EXPECT_FALSE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::weak_ptr<Base> baseRes1{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_FALSE(isPointerContextValid());
EXPECT_THAT(baseRes1.use_count(), Eq(1));
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
}
struct TestSharedFromThis : public std::enable_shared_from_this<TestSharedFromThis> {
float x{};
explicit TestSharedFromThis(): std::enable_shared_from_this<TestSharedFromThis>() {}
template<typename S>
void serialize(S &s) {
s.value4b(x);
}
};
TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
std::shared_ptr<TestSharedFromThis> dataPtr(new TestSharedFromThis{});
std::shared_ptr<TestSharedFromThis> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});
createDeserializer().ext(resPtr, StdSmartPtr{});
clearSharedState();
auto resPtr2 = resPtr->shared_from_this();
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
EXPECT_THAT(resPtr2.use_count(), Eq(2));
}
struct CustomDeleter {
void operator()(Base*p) {
delete p;
}
};
class SerializeExtensionStdSmartUniquePtr:public SerializeExtensionStdSmartSharedPtr{};
TEST_F(SerializeExtensionStdSmartUniquePtr, WithCustomDeleter) {
std::unique_ptr<Base, CustomDeleter> dataPtr(new Derived{87,7});
std::unique_ptr<Base, CustomDeleter> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});
createDeserializer().ext(resPtr, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
EXPECT_THAT(dynamic_cast<Derived *>(resPtr.get()), ::testing::NotNull());
}

View File

@@ -20,9 +20,10 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_stack.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/std_stack.h>
using StdStack = bitsery::ext::StdStack;

View File

@@ -0,0 +1,129 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
#if __cplusplus > 201402L
#include <bitsery/ext/std_tuple.h>
template<typename T, size_t N>
using OverloadValue = bitsery::ext::OverloadValue<T, N>;
TEST(SerializeExtensionStdTuple, UseDefaultSerializeFunction) {
std::tuple<MyStruct1, MyStruct2> t1{MyStruct1{-789, 45}, MyStruct2{MyStruct2::MyEnum::V3, MyStruct1{}}};
std::tuple<MyStruct1, MyStruct2> r1{};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdTuple{});
ctx.createDeserializer().ext(r1, bitsery::ext::StdTuple{});
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdTuple, ValueTypesCanBeSerializedWithLambdaAndOrCallableObject) {
std::tuple<float, int32_t> t1{123.456f, -898754656};
std::tuple<float, int32_t> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdTuple{
[](auto& s1, float& o1) {
s1.value4b(o1);
},
OverloadValue<int32_t, 4>{}
});
};
exec(ctx.createSerializer(), t1);
exec(ctx.createDeserializer(), r1);
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction) {
std::tuple<MyStruct1, MyStruct2> t1{MyStruct1{-789, 45}, MyStruct2{MyStruct2::MyEnum::V3, MyStruct1{}}};
std::tuple<MyStruct1, MyStruct2> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdTuple{
[](auto& s1, MyStruct1& o1) {
s1.value4b(o1.i1);
//do not serialize other element, it should be 0 (default)
},
});
};
exec(ctx.createSerializer(), t1);
exec(ctx.createDeserializer(), r1);
EXPECT_THAT(std::get<1>(t1), Eq(std::get<1>(r1)));
EXPECT_THAT(std::get<0>(t1).i1, Eq(std::get<0>(r1).i1));
EXPECT_THAT(std::get<0>(t1).i2, ::testing::Ne(std::get<0>(r1).i2));
}
TEST(SerializeExtensionStdTuple, EmptyTuple) {
std::tuple<> t1{};
std::tuple<> r1{};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdTuple{});
ctx.createDeserializer().ext(r1, bitsery::ext::StdTuple{});
EXPECT_THAT(t1, Eq(r1));
}
struct NonDefaultConstructable {
explicit NonDefaultConstructable(float x) : _x{x} {}
float _x;
bool operator==(const NonDefaultConstructable& rhs) const {
return _x == rhs._x;
}
private:
friend class bitsery::Access;
NonDefaultConstructable() : _x{0.0f} {};
};
TEST(SerializeExtensionStdTuple, NonDefaultConstructable) {
std::tuple<NonDefaultConstructable> t1{34};
std::tuple<NonDefaultConstructable> r1{8};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
});
ctx.createDeserializer().ext(r1, bitsery::ext::StdTuple{
[](auto& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
});
EXPECT_THAT(t1, Eq(r1));
}
#else
#if defined(_MSC_VER)
#pragma message("Tests for StdTuple requires C++17")
#else
#warning "Tests for StdTuple requires C++17"
#endif
#endif

View File

@@ -0,0 +1,178 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#if __cplusplus > 201402L
using testing::Eq;
#include <bitsery/ext/std_variant.h>
template<typename T, size_t N>
using OverloadValue = bitsery::ext::OverloadValue<T, N>;
TEST(SerializeExtensionStdVariant, UseSerializeFunction) {
std::variant<MyStruct1, MyStruct2> t1{MyStruct1{978, 15}};
std::variant<MyStruct1, MyStruct2> r1{MyStruct2{}};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{});
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{});
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, WhenTwoIndicesWithSameTypeThenDeserializeCorrectIndex) {
std::variant<MyStruct1, MyStruct2, MyStruct1> t1{std::in_place_index_t<2>{}, MyStruct1{978, 15}};
std::variant<MyStruct1, MyStruct2, MyStruct1> r1{MyStruct2{}};
SerializationContext ctx;
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{});
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{});
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambda) {
std::variant<float, char, MyStruct1> t1{5.6f};
std::variant<float, char, MyStruct1> r1{MyStruct1{}};
SerializationContext ctx;
auto fncFloat = [](auto& s, float& v) {
s.value4b(v);
};
auto fncChar = [](auto& s, char& v) {
s.value1b(v);
};
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{fncFloat, fncChar});
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{fncFloat, fncChar});
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambdaAndOrCallableObject) {
std::variant<float, char, MyStruct1> t1{'Z'};
std::variant<float, char, MyStruct1> r1{MyStruct1{}};
SerializationContext ctx;
auto fncFloat = [](auto& s, float& v) {
s.value4b(v);
};
auto& ser = ctx.createSerializer();
ser.ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
auto& des = ctx.createDeserializer();
des.ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, CanOverloadDefaultSerializationFunction) {
std::variant<MyStruct2, MyStruct1, int32_t> t1{MyStruct1{5, 9}};
std::variant<MyStruct2, MyStruct1, int32_t> r1{MyStruct1{}};
SerializationContext ctx;
auto exec = [](auto& s, std::variant<MyStruct2, MyStruct1, int32_t>& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, MyStruct1& v) {
s.value4b(v.i1);
//do not serialize other element, it should be 0 (default)
},
OverloadValue<int32_t, 4>{}
});
};
exec(ctx.createSerializer(), t1);
exec(ctx.createDeserializer(), r1);
EXPECT_THAT(std::get<1>(r1).i2, Eq(0));
}
struct NonDefaultConstructable {
explicit NonDefaultConstructable(float x) : _x{x} {}
float _x;
bool operator==(const NonDefaultConstructable& rhs) const {
return _x == rhs._x;
}
private:
friend class bitsery::Access;
NonDefaultConstructable() : _x{0.0f} {};
};
TEST(SerializeExtensionStdVariant, CanUseNonDefaultConstructableTypes) {
std::variant<NonDefaultConstructable, MyStruct1, int32_t> t1{NonDefaultConstructable{123.456f}};
std::variant<NonDefaultConstructable, MyStruct1, int32_t> r1{MyStruct1{}};
SerializationContext ctx;
auto exec = [](auto& s, std::variant<NonDefaultConstructable, MyStruct1, int32_t>& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
OverloadValue<int32_t, 4>{}
});
};
exec(ctx.createSerializer(), t1);
exec(ctx.createDeserializer(), r1);
EXPECT_THAT(t1, Eq(r1));
}
TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState) {
std::variant<std::monostate, NonDefaultConstructable, MyStruct1> t1{};
std::variant<std::monostate, NonDefaultConstructable, MyStruct1> r1{};
SerializationContext ctx;
auto exec = [](auto& s, auto& o) {
using S = decltype(s);
s.ext(o, bitsery::ext::StdVariant{
[](S& s, NonDefaultConstructable& v) {
s.value4b(v._x);
},
});
};
exec(ctx.createSerializer(), t1);
exec(ctx.createDeserializer(), r1);
EXPECT_THAT(t1, Eq(r1));
std::variant<std::monostate> t2{};
std::variant<std::monostate> r2{};
SerializationContext ctx1;
ctx1.createSerializer().ext(t2, bitsery::ext::StdVariant{});
ctx1.createDeserializer().ext(r2, bitsery::ext::StdVariant{});
EXPECT_THAT(t2, Eq(r2));
}
#else
#if defined(_MSC_VER)
#pragma message("Tests for StdVariant requires C++17")
#else
#warning "Tests for StdVariant requires C++17"
#endif
#endif

View File

@@ -21,9 +21,10 @@
//SOFTWARE.
#include <bitsery/ext/value_range.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/value_range.h>
using namespace testing;
using bitsery::details::RangeSpec;
using bitsery::ext::BitsConstraint;

View File

@@ -21,11 +21,12 @@
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
using testing::StrEq;
using testing::ContainerEq;

View File

@@ -37,26 +37,26 @@ bool SerializeDeserializeContainerSize(SerializationContext& ctx, const size_t s
TEST(SerializeSize, WhenLengthLessThan128Then1Byte) {
SerializationContext ctx1{};
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 127));
EXPECT_THAT(ctx1.getBufferSize(), Eq(1));
EXPECT_THAT(ctx1.getBufferSize(), Eq(1u));
SerializationContext ctx2;
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 128));
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(1));
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(1u));
}
TEST(SerializeSize, WhenLengthLessThan16384Then2Bytes) {
SerializationContext ctx1;
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 16383));
EXPECT_THAT(ctx1.getBufferSize(), Eq(2));
EXPECT_THAT(ctx1.getBufferSize(), Eq(2u));
SerializationContext ctx2;
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 16384));
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(2));
EXPECT_THAT(ctx2.getBufferSize(), testing::Gt(2u));
}
TEST(SerializeSize, WhenGreaterThan16383Then4Bytes) {
SerializationContext ctx1;
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx1, 16384));
EXPECT_THAT(ctx1.getBufferSize(), Eq(4));
EXPECT_THAT(ctx1.getBufferSize(), Eq(4u));
SerializationContext ctx2;
EXPECT_TRUE(SerializeDeserializeContainerSize(ctx2, 66384));
EXPECT_THAT(ctx2.getBufferSize(), Eq(4));
}
EXPECT_THAT(ctx2.getBufferSize(), Eq(4u));
}

View File

@@ -134,7 +134,7 @@ public:
}
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
//this function returns number of bytes writen to buffer, when reading/writing size of container
//this function returns number of bytes written to buffer, when reading/writing size of container
static size_t containerSizeSerializedBytesCount(size_t elemsCount) {
if (elemsCount < 0x80u)
return 1;

View File

@@ -21,9 +21,9 @@
//SOFTWARE.
#include <bitsery/traits/string.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using namespace testing;