mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 08:13:56 +00:00
non default constructible types
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,3 +1,14 @@
|
||||
# [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.
|
||||
|
||||
### 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/*`.
|
||||
* fixed `StdSmartPtr` to correctly work with `std::unique_ptr` when it has custom deleter.
|
||||
|
||||
# [4.4.0](https://github.com/fraillt/bitsery/compare/v4.3.0...v4.4.0) (2019-01-08)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(bitsery
|
||||
LANGUAGES CXX
|
||||
VERSION 4.4.0)
|
||||
VERSION 4.5.0)
|
||||
|
||||
#======== build options ===================================
|
||||
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
|
||||
65
examples/non_default_constructible.cpp
Normal file
65
examples/non_default_constructible.cpp
Normal 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);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 4
|
||||
#define BITSERY_MINOR_VERSION 4
|
||||
#define BITSERY_MINOR_VERSION 5
|
||||
#define BITSERY_PATCH_VERSION 0
|
||||
|
||||
#define BITSERY_QUOTE_MACRO(name) #name
|
||||
|
||||
@@ -32,17 +32,28 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//this allows to call private serialize method for the class
|
||||
//just make friend it to that class
|
||||
//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
|
||||
struct Access {
|
||||
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 +67,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 +184,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,
|
||||
@@ -312,13 +323,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ... TArgs>
|
||||
void reserve(std::unordered_map<TArgs...>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename ... TArgs>
|
||||
void reserve(std::unordered_multimap<TArgs...>& 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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
//}
|
||||
#include <type_traits>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
@@ -54,8 +55,7 @@ namespace bitsery {
|
||||
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 {
|
||||
@@ -75,7 +75,7 @@ namespace bitsery {
|
||||
if (_alignBeforeData)
|
||||
des.align();
|
||||
if (exists) {
|
||||
typename T::value_type tmp{};
|
||||
auto tmp{::bitsery::Access::create<typename T::value_type>()};
|
||||
fnc(tmp);
|
||||
obj = tmp;
|
||||
} else {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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,24 +54,23 @@ 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 ... TArgs>
|
||||
void reserve(std::unordered_set<TArgs...>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(std::unordered_multiset<T>& obj, size_t size) const {
|
||||
template <typename ... TArgs>
|
||||
void reserve(std::unordered_multiset<TArgs...>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,8 +53,22 @@ 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>
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#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 {
|
||||
@@ -39,8 +41,25 @@ namespace bitsery {
|
||||
return static_cast<size_t>(std::distance(container.begin(), container.end()));
|
||||
}
|
||||
static void resize(std::forward_list<TArgs...>& container, size_t size) {
|
||||
resizeImpl(container, size, std::is_default_constructible<TValue>{});
|
||||
}
|
||||
private:
|
||||
static void resizeImpl(std::forward_list<TArgs...>& container, size_t size, std::true_type) {
|
||||
container.resize(size);
|
||||
}
|
||||
static void resizeImpl(std::forward_list<TArgs...>& 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
327
tests/not_default_constructible.cpp
Normal file
327
tests/not_default_constructible.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
//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
|
||||
struct 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;
|
||||
}
|
||||
};
|
||||
|
||||
struct HasherForNonDefaultConstructible {
|
||||
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()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -33,10 +33,10 @@
|
||||
|
||||
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,7 +117,6 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
EXPECT_THAT(range.getRequiredBits() + 1, ::testing::Lt(8));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user