8 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
68 changed files with 1831 additions and 210 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,38 @@
# [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

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
project(bitsery
LANGUAGES CXX
VERSION 4.4.0)
VERSION 4.6.0)
#======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)

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

@@ -23,7 +23,7 @@ All cross-platform requirements are enforced at compile time, so serialized data
* 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

View File

@@ -35,12 +35,16 @@ Serializer/Deserializer extensions via `ext` method (alphabetical order):
* `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)

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

@@ -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

@@ -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.
//

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

@@ -31,9 +31,16 @@ namespace bitsery {
//base class that stores container iterators, and is required for session support (for reading sessions data)
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} {
@@ -48,16 +55,15 @@ namespace bitsery {
template<typename Buffer>
class InputBufferAdapter : public BufferIterators<Buffer> {
public:
using TIterator = typename BufferIterators<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
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<Buffer>::isContiguous,
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)
@@ -104,13 +110,13 @@ namespace bitsery {
public:
using TIterator = typename BufferIterators<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
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<Buffer>::isContiguous,
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
"BufferAdapter only works with contiguous containers");
UnsafeInputBufferAdapter(TIterator begin, TIterator end) : BufferIterators<Buffer>(begin, end) {
UnsafeInputBufferAdapter(TIterator beginIt, TIterator endIt) : BufferIterators<Buffer>(beginIt, endIt) {
}
UnsafeInputBufferAdapter(TIterator begin, size_t size)

View File

@@ -124,7 +124,7 @@ namespace bitsery {
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()>{});
}

View File

@@ -25,7 +25,7 @@
#define BITSERY_BITSERY_H
#define BITSERY_MAJOR_VERSION 4
#define BITSERY_MINOR_VERSION 4
#define BITSERY_MINOR_VERSION 6
#define BITSERY_PATCH_VERSION 0
#define BITSERY_QUOTE_MACRO(name) #name

View File

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

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

@@ -96,7 +96,7 @@ namespace bitsery {
template<typename TResult, typename TUnsigned>
TResult zigZagDecode(TUnsigned v, std::true_type) const {
return (v >> 1) ^ -(v & 1);
return (v >> 1) ^ (~(v & 1) + 1); // same as -(v & 1), but no warning on VisualStudio
}
// write/read bytes one by one
@@ -149,6 +149,7 @@ namespace bitsery {
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::CompactValue, T> {
using TValue = T;
@@ -168,6 +169,14 @@ namespace bitsery {
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;
};
}
}

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"
#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

@@ -46,7 +46,8 @@ namespace bitsery {
using TElement = typename T::element_type;
static TElement *getPtr(std::unique_ptr<TElement> &obj) {
template <typename TDeleter>
static TElement *getPtr(std::unique_ptr<TElement, TDeleter> &obj) {
return obj.get();
}
@@ -61,7 +62,7 @@ namespace bitsery {
}
static constexpr PointerOwnershipType getOwnership() {
return std::is_same<std::unique_ptr<TElement>, T>::value
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
? PointerOwnershipType::Owner
: std::is_same<std::shared_ptr<TElement>, T>::value
? PointerOwnershipType::SharedOwner

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

@@ -29,6 +29,7 @@
#include <algorithm>
#include <cassert>
#include "../../details/adapter_utils.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {
@@ -305,7 +306,7 @@ namespace bitsery {
if (ptr) {
fnc(*ptr);
} else {
ptr = new typename TPtrManager<T>::TElement{};
ptr = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
fnc(*ptr);
TPtrManager<T>::assign(obj, ptr);
}
@@ -340,7 +341,7 @@ namespace bitsery {
fnc(*ptr);
sharedState = TPtrManager<T>::saveToSharedState(obj);
} else {
auto res = new typename TPtrManager<T>::TElement{};
auto res = ::bitsery::Access::createInHeap<typename TPtrManager<T>::TElement>();
fnc(*res);
sharedState = TPtrManager<T>::createSharedState(res);
}
@@ -374,6 +375,7 @@ namespace bitsery {
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContext() = default;
bool isValid() {
return isPointerSerializationValid() && isPointerDeserializationValid();
}

View File

@@ -26,6 +26,7 @@
#include <unordered_map>
#include <memory>
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
namespace bitsery {
@@ -69,7 +70,7 @@ namespace bitsery {
public:
void *create() const final {
return toBase(new TDerived{});
return toBase(::bitsery::Access::createInHeap<TDerived>());
}
void process(void *ser, void *obj) const final {

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

@@ -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;
};
@@ -76,6 +91,7 @@ namespace bitsery {
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")

View File

@@ -5,4 +5,4 @@ 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,11 +31,6 @@ 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})
@@ -46,8 +41,9 @@ foreach (TestFile ${TestSourceFiles})
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}>)
# add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
endforeach()
#======================= setup development environment ====================

View File

@@ -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

@@ -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

@@ -35,6 +35,17 @@
#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"
@@ -388,6 +399,33 @@ TEST(FlexibleSyntax, StdSmartPtr) {
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;
t1.emplace("my key", std::vector<std::string>{"very", "nice", "string"});

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

@@ -26,7 +26,6 @@
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <iostream>
#include <bitset>
#include <chrono>

View File

@@ -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

@@ -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

@@ -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

@@ -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

@@ -653,6 +653,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPoi
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) {
@@ -670,3 +671,20 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
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

@@ -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

@@ -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;