polymorphism and smart pointers

This commit is contained in:
Mindaugas
2018-08-23 08:08:36 +03:00
parent 275c4138ee
commit 54f69a5eea
22 changed files with 663 additions and 686 deletions

View File

@@ -1,8 +1,28 @@
# [4.2.2]
# [4.3.0](https://github.com/fraillt/bitsery/compare/v4.2.1...v4.3.0) (2018-08-23)
### Features
* added runtime polymorphism support for pointer like types (raw and smart pointers).
In order to enable polymorphism new **PolymorphicContext** was created. It provides capability to register classes with serializer/deserializer.
* runtime polymorphism can be customized, by replacing **StandardRTTI** from <bitsery/ext/utils/rtti_utils.h> header.
* added smart pointers support for std::unique_ptr, std::shared_ptr and std::weak_ptr via **StdSmartPtr** extension.
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, on some compilers can improve deserialization performance up to ~40%.
### Improvements
* improved serialization/deserialization performance for buffer adapters up to ~20%.
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, can improve deserialization performance up to ~50%.
* creatly improved interface for extending/implementing support for pointer like types. Now all pointer like types extends from **PointerObjectExtensionBase** and implements/configures required details.
* reimplemented **PointerOwner**, **PointerObserver**, **ReferencedByPointer**.
* reimplemented **PointerLinkingContext** to properly support shared objects and runtime polymorphism, pointer ownership for shared objects now has two states: SharedOwner e.g. std::shared_ptr and SharedObserver std::weak_ptr.
### Other notes
There is one *minor?* issue/limitation for pointer like types that uses virtual inheritance. When several pointers points to same object through different static type. it will not work correctly e.g.:
```cpp
struct Derived: virtual Base {...};
struct MyData {
std::shared_ptr<Derived> sptr;
std::weak_ptr<Base> wptddr;
}
```
In this example wptr and sptr have different static type, and *Derived* is virtually inherited from *Base*, so I get different pointer address for different types.
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)

View File

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

View File

@@ -19,7 +19,7 @@ All cross-platform requirements are enforced at compile time, so serialized data
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
* forward/backward compatibility for your types.
* raw pointers (no polymorphism yet).
* smart and raw pointers with customizable runtime polymorphism support.
* fine-grained bit-level serialization control.
* Easily extendable for any type.
* Allows flexible or/and verbose syntax for better serialization control.
@@ -105,4 +105,4 @@ This library was tested on
## License
**bitsery** is licensed under the [MIT license](LICENSE).
**bitsery** is licensed under the [MIT license](LICENSE).

View File

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

View File

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

View File

@@ -7,6 +7,7 @@
#include <bitsery/traits/vector.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/std_smart_ptr.h>
//in order to work with polymorphic types, we need to describe few steps:
@@ -18,39 +19,50 @@
using bitsery::ext::BaseClass;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerType;
using bitsery::ext::PointerObserver;
using bitsery::ext::StdSmartPtr;
//define our data structures
struct Color {
float r, g, b;
float r{}, g{}, b{};
bool operator == (const Color& o) const {
return std::tie(r, g, b) ==
std::tie(o.r, o.g, b);
}
};
struct Shape {
Color clr;
Color clr{};
virtual ~Shape() = 0;
};
Shape::~Shape() = default;
struct Circle : Shape {
int32_t radius;
int32_t radius{};
bool operator == (const Circle& o) const {
return std::tie(radius, clr) ==
std::tie(o.radius, o.clr);
}
};
struct Rectangle : Shape {
int32_t width;
int32_t height;
int32_t width{};
int32_t height{};
bool operator == (const Rectangle& o) const {
return std::tie(width, height, clr) ==
std::tie(o.width, o.height, o.clr);
}
};
struct RoundedRectangle : Rectangle {
int32_t radius;
};
struct SomeShapes {
std::unique_ptr<Shape> main;
std::vector<Shape *> list;
int32_t radius{};
bool operator == (const RoundedRectangle& o) const {
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
std::tie(o.radius, static_cast<const Rectangle&>(o));
}
};
//define serialization functions
@@ -87,11 +99,62 @@ void serialize(S &s, RoundedRectangle &o) {
s.value4b(o.radius);
}
//define our test structure
struct SomeShapes {
std::vector<std::shared_ptr<Shape>> sharedList;
std::unique_ptr<Shape> uniquePtr;
//weak ptr and refPtr will point to sharedList
std::weak_ptr<Shape> weakPtr;
Shape* refPtr;
};
//creates object, and populates some data
SomeShapes createData() {
SomeShapes data{};
{
auto tmp = new RoundedRectangle{};
tmp->height = 151572;
tmp->width = 488795;
tmp->radius = 898;
tmp->clr.r = 0.5f;
tmp->clr.g = 1.0f;
tmp->clr.b = 1.0f;
data.uniquePtr.reset(tmp);
}
{
auto tmp = new Circle{};
tmp->radius = 75987;
tmp->clr.r = 0.5f;
tmp->clr.g = 0.0f;
tmp->clr.b = 1.0f;
data.sharedList.emplace_back(tmp);
}
{
auto tmp = new Rectangle{};
tmp->height = 15157;
tmp->width = 48879;
tmp->clr.r = 1.0f;
tmp->clr.g = 0.0f;
tmp->clr.b = 0.0f;
data.sharedList.emplace_back(tmp);
}
data.weakPtr = data.sharedList[0];
data.refPtr = data.sharedList[1].get();
return data;
}
template<typename S>
void serialize(S &s, SomeShapes &o) {
s.ext(o.main, bitsery::ext::StdUniquePtr{});
s.container(o.list, 100, [&s](Shape *(&item)) {
s.ext(item, bitsery::ext::PointerOwner{});
s.ext(o.uniquePtr, StdSmartPtr{});
// to make things more interesting first serialize weakPtr and refPtr,
// even though objects that weakPtr and refPtr is serialized later,
// bitsery will work regardless
s.ext(o.weakPtr, StdSmartPtr{});
s.ext(o.refPtr, PointerObserver{});
s.container(o.sharedList, 100, [&s](std::shared_ptr<Shape> &item) {
s.ext(item, StdSmartPtr{});
});
}
@@ -101,6 +164,9 @@ void serialize(S &s, SomeShapes &o) {
namespace bitsery {
namespace ext {
//for each base class define DIRECTLY derived classes
//e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle, RoundedRectangle>
// is incorrect, because RoundedRectangle does not directly derive from Shape
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
};
@@ -129,73 +195,29 @@ using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<
using MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, TContext>;
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, TContext>;
//creates object, and populates some data
SomeShapes createData() {
SomeShapes data{};
{
auto tmp = new RoundedRectangle{};
tmp->height = 151572;
tmp->width = 488795;
tmp->radius = 898;
tmp->clr.r = 0.5f;
tmp->clr.g = 1.0f;
tmp->clr.b = 1.0f;
data.main.reset(tmp);
}
{
auto tmp = new Circle{};
tmp->radius = 75987;
tmp->clr.r = 0.5f;
tmp->clr.g = 0.0f;
tmp->clr.b = 1.0f;
data.list.push_back(tmp);
}
{
auto tmp = new Rectangle{};
tmp->height = 15157;
tmp->width = 48879;
tmp->clr.r = 1.0f;
tmp->clr.g = 0.0f;
tmp->clr.b = 0.0f;
data.list.push_back(tmp);
}
return data;
}
//checks if deserialized data is equal
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
{
auto d = dynamic_cast<RoundedRectangle *>(data.main.get());
auto r = dynamic_cast<RoundedRectangle *>(res.main.get());
auto d = dynamic_cast<RoundedRectangle *>(data.uniquePtr.get());
auto r = dynamic_cast<RoundedRectangle *>(res.uniquePtr.get());
assert(r != nullptr);
assert(d->clr.r == r->clr.r);
assert(d->clr.g == r->clr.g);
assert(d->clr.b == r->clr.b);
assert(d->radius == r->radius);
assert(d->width == r->width);
assert(d->height == r->height);
assert(*d == *r);
}
{
auto d = dynamic_cast<Circle *>(data.list[0]);
auto r = dynamic_cast<Circle *>(res.list[0]);
auto d = dynamic_cast<Circle *>(data.sharedList[0].get());
auto r = dynamic_cast<Circle *>(res.sharedList[0].get());
assert(r != nullptr);
assert(d->clr.r == r->clr.r);
assert(d->clr.g == r->clr.g);
assert(d->clr.b == r->clr.b);
assert(d->radius == r->radius);
assert(*d == *r);
}
{
auto d = dynamic_cast<Rectangle *>(data.list[1]);
auto r = dynamic_cast<Rectangle *>(res.list[1]);
auto d = dynamic_cast<Rectangle *>(data.sharedList[1].get());
auto r = dynamic_cast<Rectangle *>(res.sharedList[1].get());
assert(r != nullptr);
assert(d->clr.r == r->clr.r);
assert(d->clr.g == r->clr.g);
assert(d->clr.b == r->clr.b);
assert(d->width == r->width);
assert(d->height == r->height);
assert(*d == *r);
}
assert(res.weakPtr.lock().get() == res.sharedList[0].get());
assert(res.refPtr == res.sharedList[1].get());
}
int main() {
@@ -236,12 +258,12 @@ int main() {
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
//also check for dangling pointers, after deserialization
assert(std::get<0>(ctx).isValid());
// clear shared state from pointer linking context,
// it is only required if there are any pointers that manage shared state, e.g. std::shared_ptr
assert(res.weakPtr.use_count() == 2);//one in sharedList and one in pointer linking context
std::get<0>(ctx).clearSharedState();
assert(res.weakPtr.use_count() == 1);
}
assertSameShapes(data, res);
//delete raw pointers
for (auto &s:res.list)
delete s;
for (auto &s:data.list)
delete s;
return 0;
}
}

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_BUFFER_H
#define BITSERY_ADAPTER_BUFFER_H
@@ -46,7 +45,6 @@ namespace bitsery {
TIterator endIt;
};
template<typename Buffer>
class InputBufferAdapter : public BufferIterators<Buffer> {
public:
@@ -66,32 +64,6 @@ namespace bitsery {
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
}
template<typename T>
void read(T &data) {
//for optimization
auto tmp = this->posIt;
this->posIt += sizeof(T);
if (std::distance(this->posIt, this->endIt) >= 0) {
data = *reinterpret_cast<const T *>(std::addressof(*tmp));
// auto src = std::addressof(*tmp);
//// std::memcpy(&data, src, sizeof(T));
// switch (sizeof(T)) {
// case 1: std::memcpy(&data, src, sizeof(T)); break;
// case 2: std::memcpy(&data, src, sizeof(T)); break;
// case 4: std::memcpy(&data, src, sizeof(T)); break;
// case 8: std::memcpy(&data, src, sizeof(T)); break;
// case 16: std::memcpy(&data, src, sizeof(T)); break;
// }
} else {
this->posIt -= sizeof(T);
data = {};
if (error() == ReaderError::NoError)
setError(ReaderError::DataOverflow);
}
}
void read(TValue *data, size_t size) {
//for optimization
auto tmp = this->posIt;
@@ -145,16 +117,6 @@ namespace bitsery {
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
}
template<typename T>
void read(T &data) {
//for optimization
auto tmp = this->posIt;
this->posIt += sizeof(T);
assert(std::distance(this->posIt, this->endIt) >= 0);
data = *reinterpret_cast<const T *>(std::addressof(*tmp));
}
void read(TValue *data, size_t size) {
//for optimization
auto tmp = this->posIt;
@@ -179,7 +141,6 @@ namespace bitsery {
ReaderError err = ReaderError::NoError;
};
template<typename Buffer>
class OutputBufferAdapter {
public:
@@ -198,11 +159,6 @@ namespace bitsery {
init(TResizable{});
}
template<typename T>
void write(const T &data) {
writeInternal<T>(data, TResizable{});
}
void write(const TValue *data, size_t size) {
writeInternal(data, size, TResizable{});
}
@@ -235,53 +191,6 @@ namespace bitsery {
_outIt = std::begin(*_buffer);
}
template<typename T>
void writeInternal(const T &data, std::true_type) {
//optimization
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
if (std::distance(_outIt , _end) >= static_cast<TDistance>(size)) {
*reinterpret_cast<T*>(std::addressof(*tmp)) = data;
_outIt += sizeof(T);
#else
auto tmp = _outIt;
_outIt += sizeof(T);
if (std::distance(_outIt, _end) >= 0) {
*reinterpret_cast<T *>(std::addressof(*tmp)) = data;
auto x = reinterpret_cast<T *>(std::addressof(*tmp));
*x = data;
// auto dst = std::addressof(*tmp);
//// std::memcpy(dst, &data, sizeof(T));
//
// switch (sizeof(T)) {
// case 1: std::memcpy(dst, &data, sizeof(T)); break;
// case 2: std::memcpy(dst, &data, sizeof(T)); break;
// case 4: std::memcpy(dst, &data, sizeof(T)); break;
// case 8: std::memcpy(dst, &data, sizeof(T)); break;
// case 16: std::memcpy(dst, &data, sizeof(T)); break;
// }
#endif
} else {
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
#else
_outIt -= sizeof(T);
#endif
//get current position before invalidating iterators
const auto pos = std::distance(std::begin(*_buffer), _outIt);
//increase container size
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
//restore iterators
_end = std::end(*_buffer);
_outIt = std::next(std::begin(*_buffer), pos);
writeInternal(data, std::true_type{});
}
}
void writeInternal(const TValue *data, const size_t size, std::true_type) {
//optimization
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
@@ -321,15 +230,6 @@ namespace bitsery {
_end = std::end(*_buffer);
}
template<typename T>
void writeInternal(const T &data, std::false_type) {
//optimization
auto tmp = _outIt;
_outIt += sizeof(T);
assert(std::distance(_outIt, _end) >= 0);
*reinterpret_cast<T *>(std::addressof(*tmp)) = data;
}
void writeInternal(const TValue *data, size_t size, std::false_type) {
//optimization
auto tmp = _outIt;
@@ -339,7 +239,6 @@ namespace bitsery {
}
};
}
#endif //BITSERY_ADAPTER_BUFFER_H

View File

@@ -20,8 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_READER_H
#define BITSERY_ADAPTER_READER_H
@@ -29,7 +27,6 @@
#include <algorithm>
#include <cstring>
namespace bitsery {
template <typename TReader>
@@ -63,22 +60,18 @@ namespace bitsery {
~AdapterReader() noexcept = default;
template<size_t SIZE, typename T>
void readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
//do something with it, because the way it is implemented in buffer/stream adapter is probably UB,
//implementing it in non UB fassion, has no benefit and can be completely remove instead
// directReadValue(v, SwapTag{});
directReadBuffer(&v,1, SwapTag{});
directRead(&v, 1);
}
template<size_t SIZE, typename T>
void readBuffer(T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
directReadBuffer(buf, count, SwapTag{});
directRead(buf, count);
}
template<typename T>
@@ -124,31 +117,26 @@ namespace bitsery {
InputAdapter _inputAdapter;
typename std::conditional<Config::BufferSessionsEnabled,
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
_session;
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
template <typename T>
void directReadValue(T& v, std::true_type) {
_inputAdapter.read(v);
v = details::swap(v);
}
template <typename T>
void directReadValue(T& v, std::false_type) {
_inputAdapter.read(v);
template<typename T>
void directRead(T *v, size_t count) {
static_assert(!std::is_const<T>::value, "");
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
//swap each byte if nessesarry
_swapDataBits(v, count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
}
template<typename T>
void directReadBuffer(T *v, size_t count, std::true_type) {
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
void _swapDataBits(T *v, size_t count, std::true_type) {
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
}
template<typename T>
void directReadBuffer(T *v, size_t count, std::false_type) {
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
void _swapDataBits(T *, size_t , std::false_type) {
//empty function because no swap is required
}
};
@@ -205,7 +193,6 @@ namespace bitsery {
}
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");

View File

@@ -20,8 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_WRITER_H
#define BITSERY_ADAPTER_WRITER_H
@@ -30,7 +28,6 @@
#include <cassert>
#include <utility>
namespace bitsery {
template <typename Config>
@@ -100,7 +97,6 @@ namespace bitsery {
//helper type for default config
using MeasureSize = BasicMeasureSize<DefaultConfig>;
template <typename TWriter>
class AdapterWriterBitPackingWrapper;
@@ -135,17 +131,15 @@ namespace bitsery {
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
//do something with it, because the way it is implemented in buffer/stream adapter is probably UB,
//implementing it in non UB fassion, has no benefit and can be completely remove instead
// directWriteValue(v, SwapTag{});
directWriteBuffer(&v, 1, SwapTag{});
directWrite(&v, 1);
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
directWriteBuffer(buf, count, SwapTag{});
directWrite(buf, count);
}
template<typename T>
@@ -176,27 +170,16 @@ namespace bitsery {
_session.end(*this);
}
const OutputAdapter& adapter() const {
return _outputAdapter;
}
private:
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
using SwapTag = std::integral_constant<bool, Config::NetworkEndianness != details::getSystemEndianness()>;
template <typename T>
void directWriteValue(const T& v, std::true_type) {
_outputAdapter.write(details::swap(v));
}
template <typename T>
void directWriteValue(const T& v, std::false_type) {
_outputAdapter.write(v);
template<typename T>
void directWrite(T &&v, size_t count) {
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
}
template<typename T>
void directWriteBuffer(const T *v, size_t count, std::true_type) {
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
std::for_each(v, std::next(v, count), [this](const T &v) {
const auto res = details::swap(v);
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
@@ -204,7 +187,7 @@ namespace bitsery {
}
template<typename T>
void directWriteBuffer(const T *v, size_t count, std::false_type) {
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
}

View File

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

View File

@@ -25,7 +25,6 @@
#include <cassert>
#include "../traits/core/traits.h"
#include "inheritance.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
@@ -36,13 +35,13 @@ namespace bitsery {
namespace pointer_details {
template <typename T>
template<typename T>
struct PtrOwnerManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T& obj){
static TElement* getPtr(T &obj) {
return obj;
}
@@ -61,18 +60,18 @@ namespace bitsery {
}
};
template <typename T>
template<typename T>
struct PtrObserverManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
//observer must return reference to pointer, so that it could be updated later
static TElement*& getPtrRef(T& obj){
static TElement*& getPtrRef(T& obj) {
return obj;
}
static TElement* getPtr(T& obj){
static TElement* getPtr(T& obj) {
return obj;
}
@@ -91,57 +90,80 @@ namespace bitsery {
};
template <typename T>
template<typename T>
struct NonPtrManager {
static_assert(!std::is_pointer<T>::value, "");
using TElement = T;
static TElement* getPtr(T& obj){
static TElement* getPtr(T& obj) {
return &obj;
}
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Owner;
}
// this code is unreachable for reference type, but is necessary to compile
// LCOV_EXCL_START
static void assign(T& obj, TElement* valuePtr) {}
static void clear(T& obj) {}
static void assign(T& , TElement* ) {}
static void clear(T& ) {}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI {
template<typename TBase>
static size_t get(TBase& ) {
return 0;
}
template<typename TBase>
static constexpr size_t get() {
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return false;
}
};
}
template <typename RTTI>
template<typename RTTI>
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrObserverManager, PolymorphicContext, NoRTTI>;
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
class ReferencedByPointer: public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, NoRTTI>{
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
public:
ReferencedByPointer():pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, NoRTTI>(PointerType::NotNull) {}
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
};
}
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T *> {
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
@@ -150,7 +172,7 @@ namespace bitsery {
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T *> {
struct ExtensionTraits<ext::PointerObserver, T*> {
//although pointer observer doesn't serialize anything, but we still add value overload support to be consistent with pointer owners
//observer only writes/reads pointer id from pointer linking context
using TValue = T;
@@ -171,5 +193,4 @@ namespace bitsery {
}
#endif //BITSERY_EXT_POINTER_H

View File

@@ -1,6 +1,6 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,11 @@
#include <cassert>
#include "../traits/core/traits.h"
#include "inheritance.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
#include <memory>
#include <iostream>
namespace bitsery {
namespace ext {
@@ -41,24 +37,24 @@ namespace bitsery {
//further code is for managing shared ownership
//do not nest this type in pointer manager class itself, because it will be different type for different T
struct SharedPtrSharedState: pointer_utils::PointerSharedStateBase {
std::shared_ptr<void> obj;
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase {
std::shared_ptr<void> obj{};
};
template <typename T>
template<typename T>
struct SmartPtrOwnerManager {
using TElement = typename T::element_type;
static TElement* getPtr(std::unique_ptr<TElement>& obj){
static TElement *getPtr(std::unique_ptr<TElement> &obj) {
return obj.get();
}
static TElement* getPtr(std::shared_ptr<TElement>& obj){
static TElement *getPtr(std::shared_ptr<TElement> &obj) {
return obj.get();
}
static TElement* getPtr(std::weak_ptr<TElement>& obj){
static TElement *getPtr(std::weak_ptr<TElement> &obj) {
if (auto ptr = obj.lock())
return ptr.get();
return nullptr;
@@ -67,19 +63,21 @@ namespace bitsery {
static constexpr PointerOwnershipType getOwnership() {
return std::is_same<std::unique_ptr<TElement>, T>::value
? PointerOwnershipType::Owner
: PointerOwnershipType::Shared;
: std::is_same<std::shared_ptr<TElement>, T>::value
? PointerOwnershipType::SharedOwner
: PointerOwnershipType::SharedObserver;
}
static void clear(T& obj) {
static void clear(T &obj) {
obj.reset();
}
static void assign(T& obj, TElement* valuePtr) {
static void assign(T &obj, TElement *valuePtr) {
obj.reset(valuePtr);
}
//this is used, when old object exists and is the same type
static std::unique_ptr<pointer_utils::PointerSharedStateBase> saveToSharedState(T& obj) {
static std::unique_ptr<pointer_utils::PointerSharedStateBase> saveToSharedState(T &obj) {
auto state = new SharedPtrSharedState{};
//to work with weak_ptr and shared_ptr create new std::shared_ptr
state->obj = std::shared_ptr<TElement>(obj);
@@ -87,61 +85,36 @@ namespace bitsery {
}
//this is used, when old object doesn't exists or is not the same type
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedState(TElement* valuePtr) {
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedState(TElement *valuePtr) {
auto state = new SharedPtrSharedState{};
state->obj = std::shared_ptr<TElement>(valuePtr);
return std::unique_ptr<pointer_utils::PointerSharedStateBase>{state};
}
static void loadFromSharedState(pointer_utils::PointerSharedStateBase* ctx, T& obj) {
auto state = dynamic_cast<SharedPtrSharedState*>(ctx);
static void loadFromSharedState(pointer_utils::PointerSharedStateBase *ctx, T &obj) {
auto state = dynamic_cast<SharedPtrSharedState *>(ctx);
//reinterpret_pointer_cast is only since c++17
auto p = reinterpret_cast<TElement*>(state->obj.get());
auto p = reinterpret_cast<TElement *>(state->obj.get());
obj = std::shared_ptr<TElement>(state->obj, p);
}
};
}
template <typename RTTI>
using StdUniquePtrBase = pointer_utils::PointerObjectExtensionBase<
template<typename RTTI>
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
//helper type for convienience
using StdUniquePtr = StdUniquePtrBase<StandardRTTI>;
template <typename RTTI>
using StdSharedPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
//helper type for convienience
using StdSharedPtr = StdSharedPtrBase<StandardRTTI>;
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
}
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdUniquePtrBase<RTTI>, std::unique_ptr<T>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdSharedPtrBase<RTTI>, std::shared_ptr<T>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdSharedPtrBase<RTTI>, std::weak_ptr<T>> {
using TValue = T;
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T> {
using TValue = typename T::element_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
@@ -150,7 +123,6 @@ namespace bitsery {
}
}
#endif //BITSERY_EXT_STD_SMART_PTR_H

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_POINTER_UTILS_H
#define BITSERY_POINTER_UTILS_H
@@ -40,27 +39,96 @@ namespace bitsery {
NotNull
};
// Observer - not responsible for pointer lifetime management.
// Owner - only ONE owner is responsible for this pointers creation/destruction
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer creation/destruction
// requires additional context to manage shared owners themselves.
// SharedOwner actually manages life time e.g. std::shared_ptr
// SharedObserver do not manage life time of the pointer, but can observe shared state .e.. std::weak_ptr
// and differently from Observer, creates new object if necessary and saves to shared state
enum class PointerOwnershipType : uint8_t {
//is not responsible for pointer lifetime management.
Observer,
//only ONE owner is responsible for this pointers creation/destruction
Owner,
//MANY shared owners is responsible for pointer creation/destruction
//requires additional context to manage shared owners themselves.
Shared
Observer,
Owner,
SharedOwner,
SharedObserver
};
//forward declaration
class PointerLinkingContext;
namespace pointer_utils {
enum SharedSerializationStatus {
NotSerialized,
SerializedWeak,
SerializedShared
//this class is used to store context for shared ptr owners
struct PointerSharedStateBase {
virtual ~PointerSharedStateBase() = default;
};
//PLC info is internal classes for serializer, and deserializer
struct PLCInfo {
explicit PLCInfo(PointerOwnershipType ownershipType_)
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
PointerOwnershipType ownershipType;
bool isSharedProcessed;
};
struct PLCInfoSerializer: PLCInfo {
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_), id{id_} {}
size_t id;
};
struct PLCInfoDeserializer : PLCInfo {
PLCInfoDeserializer(void *ptr, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_),
ownerPtr{ptr} {};
//need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
PLCInfoDeserializer& operator =(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer& operator =(PLCInfoDeserializer&&) = default;
void processOwner(void *ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto &o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void *(&ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.emplace_back(ptr);
}
}
void *ownerPtr;
std::vector<std::reference_wrapper<void *>> observersList{};
std::unique_ptr<PointerSharedStateBase> sharedState{};
};
void updatePLCInfo(PLCInfo &ptrInfo, PointerOwnershipType ptrType) {
//do nothing for observer
if (ptrType == PointerOwnershipType::Observer)
return;
if (ptrInfo.ownershipType == PointerOwnershipType::Observer) {
//set ownership type
ptrInfo.ownershipType = ptrType;
return;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::SharedOwner || ptrType == PointerOwnershipType::SharedObserver);
//check if need to update to SharedOwner
if (ptrType == PointerOwnershipType::SharedOwner)
ptrInfo.ownershipType = ptrType;
//mark that object already processed, so we do not serialize/deserialize duplicate objects
ptrInfo.isSharedProcessed = true;
}
class PointerLinkingContextSerialization {
public:
explicit PointerLinkingContextSerialization()
@@ -77,35 +145,14 @@ namespace bitsery {
~PointerLinkingContextSerialization() = default;
struct PointerInfo {
PointerInfo(size_t id_, PointerOwnershipType ownershipType_)
: id{id_},
ownershipType{ownershipType_},
sharedCount{0} {};
size_t id;
PointerOwnershipType ownershipType;
size_t sharedCount;
};
const PointerInfo &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PointerInfo{_currId + 1u, ptrType});
const PLCInfoSerializer &getInfoByPtr(const void *ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{_currId + 1u, ptrType});
auto &ptrInfo = res.first->second;
if (res.second) {
++_currId;
return ptrInfo;
}
//ptr already exists
//for observer return success
if (ptrType == PointerOwnershipType::Observer)
return ptrInfo;
//set owner and return success
if (ptrInfo.ownershipType == PointerOwnershipType::Observer) {
ptrInfo.ownershipType = ptrType;
return ptrInfo;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::Shared);
ptrInfo.sharedCount++;
updatePLCInfo(ptrInfo, ptrType);
return ptrInfo;
}
@@ -113,22 +160,18 @@ namespace bitsery {
//we cannot serialize pointers, if we haven't serialized objects themselves
bool isPointerSerializationValid() const {
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
[](const std::pair<const void *, PointerInfo> &p) {
return p.second.ownershipType != PointerOwnershipType::Observer;
[](const std::pair<const void *, PLCInfoSerializer> &p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
private:
size_t _currId;
std::unordered_map<const void *, PointerInfo> _ptrMap;
std::unordered_map<const void *, PLCInfoSerializer> _ptrMap;
};
//this class is used to store context for shared ptr owners
struct PointerSharedStateBase {
virtual ~PointerSharedStateBase() = default;
};
class PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContextDeserialization()
@@ -144,107 +187,51 @@ namespace bitsery {
~PointerLinkingContextDeserialization() = default;
struct PointerInfo {
PointerInfo(size_t id_, void *ptr, PointerOwnershipType ownershipType_)
: id{id_},
ownershipType{ownershipType_},
ownerPtr{ptr},
observersList{},
sharedContext{},
sharedCount{}
{};
PointerInfo(const PointerInfo &) = delete;
PointerInfo &operator=(const PointerInfo &) = delete;
PointerInfo(PointerInfo &&) = default;
PointerInfo &operator=(PointerInfo &&) = default;
~PointerInfo() = default;
void processOwner(void *ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto &o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void *(&ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.push_back(ptr);
}
}
size_t id;
PointerOwnershipType ownershipType;
void *ownerPtr;
std::vector<std::reference_wrapper<void *>> observersList;
std::unique_ptr<PointerSharedStateBase> sharedContext;
size_t sharedCount;
};
PointerInfo &getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PointerInfo{id, nullptr, ptrType});
PLCInfoDeserializer &getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType});
auto &ptrInfo = res.first->second;
if (!res.second) {
//id already exists
//for observer return success
if (ptrType == PointerOwnershipType::Observer)
return ptrInfo;
//set owner and return success
if (ptrInfo.ownershipType == PointerOwnershipType::Observer) {
ptrInfo.ownershipType = ptrType;
return ptrInfo;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::Shared);
ptrInfo.sharedCount++;
}
if (!res.second)
updatePLCInfo(ptrInfo, ptrType);
return ptrInfo;
}
void clearSharedState() {
_idMap.clear();
for (auto &item: _idMap)
item.second.sharedState.reset();
}
//valid, when all pointers has owners
bool isPointerDeserializationValid() const {
return std::all_of(_idMap.begin(), _idMap.end(), [](const std::pair<const size_t, PointerInfo> &p) {
return p.second.ownershipType != PointerOwnershipType::Observer;
});
return std::all_of(_idMap.begin(), _idMap.end(),
[](const std::pair<const size_t, PLCInfoDeserializer> &p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
private:
std::unordered_map<size_t, PointerInfo> _idMap;
std::unordered_map<size_t, PLCInfoDeserializer> _idMap;
};
template<template <typename> typename TPtrManager, template <typename> typename TPolymorphicContext, typename RTTI>
template<template<typename> class TPtrManager,
template<typename> class TPolymorphicContext, typename RTTI>
class PointerObjectExtensionBase {
public:
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable) :
_ptrType{ptrType}
{}
_ptrType{ptrType} {}
template<typename Ser, typename Writer, typename T, typename Fnc>
void serialize(Ser &ser, Writer &w, const T &obj, Fnc &&fnc) const {
auto ptr = TPtrManager<T>::getPtr(const_cast<T&>(obj));
auto ptr = TPtrManager<T>::getPtr(const_cast<T &>(obj));
if (ptr) {
auto ctx = ser.template context<PointerLinkingContext>();
assert(ctx != nullptr);
auto &ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
details::writeSize(w, ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (ptrInfo.sharedCount == 0)
if (!ptrInfo.isSharedProcessed)
serializeImpl(ser, ptr, std::forward<Fnc>(fnc), w, IsPolymorphic<T>{});
}
} else {
@@ -273,18 +260,20 @@ namespace bitsery {
}
private:
template <typename T>
struct IsPolymorphic:std::integral_constant<bool, RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
};
template <typename T>
const void* getBasePtr(const T* ptr) const {
template<typename T>
const void *getBasePtr(const T *ptr) const {
// todo implement handling of types with virtual inheritance
// this is required to correctly track same shared object, e.g. shared_ptr<Base> and shared_ptr<Derived>
// this is required to correctly track same object, when one object is derived and other is base class
// e.g. shared_ptr<Base> and weak_ptr<Derived> or pointer observer Base*
return ptr;
}
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
void serializeImpl(Ser &ser, TPtr &ptr, Fnc &&, Writer &w, std::true_type) const {
const auto &ctx = ser.template context<TPolymorphicContext<RTTI>>();
@@ -295,21 +284,23 @@ namespace bitsery {
void serializeImpl(Ser &, TPtr &ptr, Fnc &&fnc, Writer &, std::false_type) const {
fnc(*ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx->deserialize(des,r, TPtrManager<T>::getPtr(obj),
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, this](typename TPtrManager<T>::TElement *valuePtr) {
TPtrManager<T>::assign(obj, valuePtr);
});
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Owner>) const {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(*ptr);
@@ -320,14 +311,15 @@ namespace bitsery {
}
ptrInfo.processOwner(ptr);
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Shared> ) const {
auto& sharedState = ptrInfo.sharedContext;
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&,
Reader &r, std::true_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
if (!sharedState) {
const auto &ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx->deserialize(des,r, TPtrManager<T>::getPtr(obj),
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
[&obj, &sharedState](typename TPtrManager<T>::TElement *valuePtr) {
sharedState = TPtrManager<T>::createSharedState(valuePtr);
});
@@ -337,11 +329,12 @@ namespace bitsery {
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader>
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type polymorph, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Shared>) const {
auto& sharedState = ptrInfo.sharedContext;
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, std::false_type ,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>) const {
auto &sharedState = ptrInfo.sharedState;
if (!sharedState) {
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
fnc(*ptr);
@@ -355,10 +348,19 @@ namespace bitsery {
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorph>
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &des, T &obj, Fnc &&fnc,
Reader &r, isPolymorph polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedObserver>) const {
deserializeImpl(ptrInfo, des, obj, fnc, r, polymorph,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorphic>
void deserializeImpl(PointerLinkingContextDeserialization::PointerInfo& ptrInfo, Des &, T &obj, Fnc &&fnc,
Reader &, isPolymorphic, std::integral_constant<PointerOwnershipType, PointerOwnershipType::Observer>) const {
void deserializeImpl(PLCInfoDeserializer &ptrInfo, Des &, T &obj, Fnc &&,
Reader &, isPolymorphic,
std::integral_constant<PointerOwnershipType, PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void *&>(TPtrManager<T>::getPtrRef(obj)));
}

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
@@ -61,9 +60,8 @@ namespace bitsery {
class PolymorphicHandlerBase {
public:
virtual void *create() const = 0;
virtual void process(void *ser, void *obj) const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
@@ -90,7 +88,6 @@ namespace bitsery {
};
template<typename RTTI>
class PolymorphicContext {
private:
@@ -137,17 +134,18 @@ namespace bitsery {
if (_baseToDerivedMap.emplace(key, std::unique_ptr<PolymorphicHandlerBase>(
new PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>{})).second)
_baseToDerivedArray[key.baseHash].push_back(key.derivedHash);
};
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::true_type) {
//cannot add abstract class
};
}
std::unordered_map<BaseToDerivedKey, std::unique_ptr<PolymorphicHandlerBase>, BaseToDerivedKeyHashier> _baseToDerivedMap;
std::unordered_map<BaseToDerivedKey, std::unique_ptr<PolymorphicHandlerBase>, BaseToDerivedKeyHashier> _baseToDerivedMap{};
// this will allow convert from platform specific type information, to platform independent base->derived index
// this only works if all polymorphic relationships (PolymorphicBaseClass<TBase> -> PolymorphicDerivedClasses<TDerived...>)
// is equal between platforms.
std::unordered_map<size_t, std::vector<size_t>> _baseToDerivedArray;
std::unordered_map<size_t, std::vector<size_t>> _baseToDerivedArray{};
public:
@@ -173,15 +171,15 @@ namespace bitsery {
auto it = _baseToDerivedMap.find(key);
assert(it != _baseToDerivedMap.end());
//convert derived hash to derived index, to make it work in cross-platform environment
auto &vec = _baseToDerivedArray.find(key.baseHash)->second;
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(), key.derivedHash)));
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(),
key.derivedHash)));
details::writeSize(writer, derivedIndex);
//serialize
it->second->process(&ser, &obj);
};
}
template<typename Deserializer, typename Reader, typename TBase, typename TAssignFnc>
void deserialize(Deserializer &des, Reader &reader, TBase *obj, TAssignFnc assignFnc) {
@@ -199,14 +197,14 @@ namespace bitsery {
BaseToDerivedKey{RTTI::template get<TBase>(), derivedHash})->second;
//if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
obj = static_cast<TBase*>(handler->create());
obj = static_cast<TBase *>(handler->create());
assignFnc(obj);
}
handler->process(&des, obj);
} else
reader.setError(ReaderError::InvalidPointer);
};
}
};
}

View File

@@ -20,7 +20,6 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_RTTI_UTILS_H
#define BITSERY_RTTI_UTILS_H
@@ -41,6 +40,7 @@ namespace bitsery {
static size_t get(TBase &obj) {
return typeid(obj).hash_code();
}
template<typename TBase>
static constexpr size_t get() {
return typeid(TBase).hash_code();
@@ -59,28 +59,6 @@ namespace bitsery {
};
struct NoRTTI {
template<typename TBase>
static size_t get(TBase &obj) {
return 0;
}
template<typename TBase>
static constexpr size_t get() {
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived *cast(TBase *obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived *>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return false;
}
};
}
}

View File

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

View File

@@ -38,11 +38,15 @@ endif()
enable_testing()
foreach(TestFile ${TestSourceFiles})
foreach (TestFile ${TestSourceFiles})
get_filename_component(TestName ${TestFile} NAME_WE)
set(TestName bitsery.test.${TestName})
add_executable(${TestName} ${TestFile})
target_link_libraries(${TestName} PRIVATE GTest::Main Bitsery::bitsery)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
endif()
add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
endforeach()
@@ -55,7 +59,7 @@ if (ParentDir)
file(GLOB_RECURSE HeadersForIDE ${ParentDir}/include/bitsery/*.h)
# create dummy target IDE
file(WRITE ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp "//generated by CMake to create dummy target with all includes for IDE.")
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp )
add_library(bitsery.dummy_for_ide ${CMAKE_BINARY_DIR}/dummy_for_ide.cpp)
# add headers so IDE correctly show them
target_sources(bitsery.dummy_for_ide PRIVATE ${HeadersForIDE} serialization_test_utils.h)
target_link_libraries(bitsery.dummy_for_ide PRIVATE GTest::Main Bitsery::bitsery)

View File

@@ -34,11 +34,11 @@
#include <bitsery/flexible/unordered_map.h>
#include <bitsery/flexible/set.h>
#include <bitsery/flexible/unordered_set.h>
#include <bitsery/flexible/memory.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
TEST(FlexibleSyntax, FundamentalTypesAndBool) {
@@ -46,9 +46,9 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx{};
ctx.createSerializer().archive(ti,te,tf,td,tb);
ctx.createSerializer().archive(ti, te, tf, td, tb);
//result
int ri{};
@@ -56,7 +56,7 @@ TEST(FlexibleSyntax, FundamentalTypesAndBool) {
float rf{};
double rd{};
bool rb{};
ctx.createDeserializer().archive(ri,re,rf,rd,rb);
ctx.createDeserializer().archive(ri, re, rf, rd, rb);
//test
EXPECT_THAT(ri, Eq(ti));
@@ -71,9 +71,9 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx;
auto& ser = ctx.createSerializer();
auto &ser = ctx.createSerializer();
ser.object(ti);
ser.object(te);
ser.object(tf);
@@ -86,7 +86,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
float rf{};
double rd{};
bool rb{};
auto& des = ctx.createDeserializer();
auto &des = ctx.createDeserializer();
des.object(ri);
des.object(re);
des.object(rf);
@@ -106,9 +106,9 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
MyEnumClass te = MyEnumClass::E4;
float tf = 485.042f;
double td = -454184.48445;
bool tb=true;
bool tb = true;
SerializationContext ctx;
auto& ser = ctx.createSerializer();
auto &ser = ctx.createSerializer();
ser.value<sizeof(ti)>(ti);
ser.archive(te, tf, td);
ser.object(tb);
@@ -119,7 +119,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
float rf{};
double rd{};
bool rb{};
auto& des = ctx.createDeserializer();
auto &des = ctx.createDeserializer();
des.archive(ri, re, rf);
des.value8b(rd);
des.object(rb);
@@ -132,28 +132,27 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
EXPECT_THAT(rb, Eq(tb));
}
template <typename T>
template<typename T>
T procArchive(const T& testData) {
SerializationContext ctx;
ctx.createSerializer().archive(testData);
T res;
T res{};
ctx.createDeserializer().archive(res);
return res;
}
template <typename T>
template<typename T>
T procArchiveWithMaxSize(const T& testData) {
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::maxSize(testData, 100));
T res;
T res{};
ctx.createDeserializer().archive(bitsery::maxSize(res, 100));
return res;
}
TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
const int t1[3]{8748,-484,45};
int r1[3]{0,0,0};
const int t1[3]{8748, -484, 45};
int r1[3]{0, 0, 0};
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::asContainer(t1));
@@ -164,7 +163,7 @@ TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
TEST(FlexibleSyntax, CStyleArrayForIntegralTypesAsText) {
const char t1[3]{"hi"};
char r1[3]{0,0,0};
char r1[3]{0, 0, 0};
SerializationContext ctx;
ctx.createSerializer().archive(bitsery::asText(t1));
@@ -184,7 +183,6 @@ TEST(FlexibleSyntax, CStyleArray) {
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
}
TEST(FlexibleSyntax, StdString) {
std::string t1{"my nice string"};
std::string t2{};
@@ -196,56 +194,51 @@ TEST(FlexibleSyntax, StdString) {
}
TEST(FlexibleSyntax, StdArray) {
std::array<int, 3> t1{8748,-484,45};
std::array<int, 3> t1{8748, -484, 45};
std::array<int, 0> t2{};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdVector) {
std::vector<int> t1{8748,-484,45};
std::vector<float> t2{5.f,0.198f};
std::vector<int> t1{8748, -484, 45};
std::vector<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdList) {
std::list<int> t1{8748,-484,45};
std::list<float> t2{5.f,0.198f};
std::list<int> t1{8748, -484, 45};
std::list<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdForwardList) {
std::forward_list<int> t1{8748,-484,45};
std::forward_list<float> t2{5.f,0.198f};
std::forward_list<int> t1{8748, -484, 45};
std::forward_list<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdDeque) {
std::deque<int> t1{8748,-484,45};
std::deque<float> t2{5.f,0.198f};
std::deque<int> t1{8748, -484, 45};
std::deque<float> t2{5.f, 0.198f};
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchive(t2), Eq(t2));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
}
TEST(FlexibleSyntax, StdQueue) {
@@ -255,7 +248,6 @@ TEST(FlexibleSyntax, StdQueue) {
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
}
TEST(FlexibleSyntax, StdPriorityQueue) {
@@ -273,7 +265,6 @@ TEST(FlexibleSyntax, StdPriorityQueue) {
r1.pop();
t1.pop();
}
}
TEST(FlexibleSyntax, StdStack) {
@@ -283,13 +274,12 @@ TEST(FlexibleSyntax, StdStack) {
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
}
TEST(FlexibleSyntax, StdUnorderedMap) {
std::unordered_map<int, int> t1;
t1.emplace(3423,624);
t1.emplace(-5484,-845);
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
@@ -297,9 +287,9 @@ TEST(FlexibleSyntax, StdUnorderedMap) {
TEST(FlexibleSyntax, StdUnorderedMultiMap) {
std::unordered_multimap<std::string, int> t1;
t1.emplace("one",624);
t1.emplace("two",-845);
t1.emplace("one",897);
t1.emplace("one", 624);
t1.emplace("two", -845);
t1.emplace("one", 897);
EXPECT_TRUE(procArchive(t1) == t1);
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
@@ -307,8 +297,8 @@ TEST(FlexibleSyntax, StdUnorderedMultiMap) {
TEST(FlexibleSyntax, StdMap) {
std::map<int, int> t1;
t1.emplace(3423,624);
t1.emplace(-5484,-845);
t1.emplace(3423, 624);
t1.emplace(-5484, -845);
EXPECT_THAT(procArchive(t1), Eq(t1));
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
@@ -316,9 +306,9 @@ TEST(FlexibleSyntax, StdMap) {
TEST(FlexibleSyntax, StdMultiMap) {
std::multimap<std::string, int> t1;
t1.emplace("one",624);
t1.emplace("two",-845);
t1.emplace("one",897);
t1.emplace("one", 624);
t1.emplace("two", -845);
t1.emplace("one", 897);
auto res = procArchive(t1);
//same key values is not ordered, and operator == compares each element at same position
@@ -376,6 +366,27 @@ TEST(FlexibleSyntax, StdMultiSet) {
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
}
TEST(FlexibleSyntax, StdSmartPtr) {
std::shared_ptr<int> dataShared1(new int{4});
std::weak_ptr<int> dataWeak1(dataShared1);
std::unique_ptr<std::string> dataUnique1{new std::string{"hello world"}};
bitsery::ext::PointerLinkingContext plctx1{};
BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext> ctx;
ctx.createSerializer(&plctx1).archive(dataShared1, dataWeak1, dataUnique1);
std::shared_ptr<int> resShared1{};
std::weak_ptr<int> resWeak1{};
std::unique_ptr<std::string> resUnique1{};
ctx.createDeserializer(&plctx1).archive(resShared1, resWeak1, resUnique1);
//clear shared state from pointer linking context
plctx1.clearSharedState();
EXPECT_TRUE(plctx1.isValid());
EXPECT_THAT(*resShared1, Eq(*dataShared1));
EXPECT_THAT(*resWeak1.lock(), Eq(*dataWeak1.lock()));
EXPECT_THAT(*resUnique1, Eq(*dataUnique1));
}
TEST(FlexibleSyntax, NestedTypes) {
std::unordered_map<std::string, std::vector<std::string>> t1;

View File

@@ -264,4 +264,6 @@ TEST_P(SerializeContainer, SizeHasVariableLength) {
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
}
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000));
//last comma is to suppress error that otherwise can be suppressed by clang/gcc with -Wgnu-zero-variadic-macro-arguments
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000),);

View File

@@ -57,7 +57,6 @@ public:
MyEnumClass *p2null = nullptr;
MyStruct1 *p3null = nullptr;
PointerLinkingContext plctx1{};
SerContext sctx1{};
@@ -92,9 +91,26 @@ TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwners
MyStruct1 *sharedPtr = &data;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::Shared).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
}
TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextIsInvalid) {
MyStruct1 data1{};
MyStruct1 data2{};
//pretend that this is shared ptr
MyStruct1 *sharedPtr1 = &data1;
MyStruct1 *sharedPtr2 = &data2;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(2));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedObserver).id, Eq(1));
EXPECT_FALSE(plctx1.isValid());
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr1, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(1));
EXPECT_THAT(plctx1.getInfoByPtr(sharedPtr2, bitsery::ext::PointerOwnershipType::SharedOwner).id, Eq(2));
EXPECT_TRUE(plctx1.isValid());
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
@@ -372,7 +388,6 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
EXPECT_THAT(pr3, Eq(&r3));
}
struct Test1Data {
std::vector<MyStruct1> vdata;
std::vector<MyStruct1 *> vptr;
@@ -451,7 +466,6 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
delete res.pi1;
}
TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOverload) {
const int32_t NEW_VALUE = 2;
const int32_t OLD_VALUE = 1;

View File

@@ -21,6 +21,7 @@
//SOFTWARE.
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <gmock/gmock.h>
@@ -29,7 +30,6 @@
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
using bitsery::ext::InheritanceContext;
using bitsery::ext::PointerLinkingContext;
using bitsery::ext::PolymorphicContext;
@@ -39,8 +39,6 @@ using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerType;
using testing::Eq;
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;

View File

@@ -20,8 +20,9 @@
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <bitsery/ext/std_smart_ptr.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_smart_ptr.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
@@ -35,8 +36,7 @@ using bitsery::ext::PolymorphicContext;
using bitsery::ext::StandardRTTI;
using bitsery::ext::PointerType;
using bitsery::ext::StdUniquePtr;
using bitsery::ext::StdSharedPtr;
using bitsery::ext::StdSmartPtr;
using bitsery::ext::PointerObserver;
using testing::Eq;
@@ -87,7 +87,6 @@ void serialize(S &s, MoreDerived &o) {
s.value1b(o.z);
}
//define relationships between base class and derived classes for runtime polymorphism
namespace bitsery {
@@ -104,10 +103,10 @@ namespace bitsery {
}
}
template <typename T>
template<typename T>
class SerializeExtensionStdSmartPtrNonPolymorphicType : public testing::Test {
public:
template <typename U>
template<typename U>
using TPtr = typename T::template TData<U>;
using TExt = typename T::TExt;
@@ -118,7 +117,6 @@ public:
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
@@ -141,10 +139,10 @@ public:
}
};
template <typename T>
template<typename T>
class SerializeExtensionStdSmartPtrPolymorphicType : public testing::Test {
public:
template <typename U>
template<typename U>
using TPtr = typename T::template TData<U>;
using TExt = typename T::TExt;
@@ -155,7 +153,6 @@ public:
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
@@ -185,15 +182,15 @@ public:
};
struct UniquePtrTest {
template <typename T>
template<typename T>
using TData = std::unique_ptr<T>;
using TExt = StdUniquePtr;
using TExt = StdSmartPtr;
};
struct SharedPtrTest {
template <typename T>
template<typename T>
using TData = std::shared_ptr<T>;
using TExt = StdSharedPtr;
using TExt = StdSmartPtr;
};
using TestingWithNonPolymorphicTypes = ::testing::Types<
@@ -208,7 +205,6 @@ using TestingWithPolymorphicTypes = ::testing::Types<
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes);
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;
using Ext = typename TestFixture::TExt;
@@ -301,13 +297,13 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerO
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t* dataObs= data.get();
auto& ser= this->createSerializer();
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
ser.ext2b(data, Ext{});
ser.ext2b(dataObs, PointerObserver{});
Ptr res{};
uint16_t* resObs = nullptr;
auto& des = this->createDeserializer();
uint16_t *resObs = nullptr;
auto &des = this->createDeserializer();
des.ext2b(res, Ext{});
des.ext2b(resObs, PointerObserver{});
@@ -319,13 +315,13 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserver
using Ext = typename TestFixture::TExt;
Ptr data{new uint16_t{3}};
uint16_t* dataObs = data.get();
auto& ser= this->createSerializer();
uint16_t *dataObs = data.get();
auto &ser = this->createSerializer();
ser.ext2b(dataObs, PointerObserver{});
ser.ext2b(data, Ext{});
Ptr res{};
uint16_t* resObs = nullptr;
auto& des = this->createDeserializer();
uint16_t *resObs = nullptr;
auto &des = this->createDeserializer();
des.ext2b(resObs, PointerObserver{});
des.ext2b(res, Ext{});
EXPECT_THAT(resObs, Eq(res.get()));
@@ -404,7 +400,6 @@ public:
using TSerializer = typename SerContext::TSerializer;
using TDeserializer = typename SerContext::TDeserializer;
TContext plctx{};
SerContext sctx{};
@@ -441,10 +436,10 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{baseData1};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
ser.ext(baseData1, StdSharedPtr{});
auto& des = createDeserializer();
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
auto &des = createDeserializer();
//1b linking context (for 1st time)
//1b dynamic type info
@@ -458,35 +453,34 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearS
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_THAT(baseRes1.use_count(), Eq(2));
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::shared_ptr<Base> baseData2{new Derived{55, 11}};
std::shared_ptr<Base> baseData21{baseData2};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
ser.ext(baseData2, StdSharedPtr{});
ser.ext(baseData21, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData21, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Base> baseRes2{};
std::shared_ptr<Base> baseRes21{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
des.ext(baseRes2, StdSharedPtr{});
des.ext(baseRes21, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes21, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
@@ -501,24 +495,23 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::weak_ptr<Base> baseData11{baseData1};
std::weak_ptr<Base> baseData12{baseData11};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
ser.ext(baseData11, StdSharedPtr{});
ser.ext(baseData12, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData12, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes12{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
des.ext(baseRes11, StdSharedPtr{});
des.ext(baseRes12, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes12, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
@@ -533,24 +526,23 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
std::weak_ptr<Base> baseData11{baseData1};
std::weak_ptr<Base> baseData2{};
auto& ser = createSerializer();
ser.ext(baseData2, StdSharedPtr{});
ser.ext(baseData11, StdSharedPtr{});
ser.ext(baseData1, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::weak_ptr<Base> baseRes11{};
std::weak_ptr<Base> baseRes2{};
auto& des = createDeserializer();
des.ext(baseRes2, StdSharedPtr{});
des.ext(baseRes11, StdSharedPtr{});
des.ext(baseRes1, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
des.ext(baseRes1, StdSmartPtr{});
auto *data = dynamic_cast<Derived *>(baseRes1.get());
EXPECT_THAT(data, ::testing::NotNull());
@@ -571,21 +563,21 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
std::shared_ptr<Base> baseData2{};
std::weak_ptr<Base> baseData3{};
std::weak_ptr<Base> baseData11{baseData1};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
ser.ext(baseData2, StdSharedPtr{});
ser.ext(baseData3, StdSharedPtr{});
ser.ext(baseData11, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
ser.ext(baseData2, StdSmartPtr{});
ser.ext(baseData3, StdSmartPtr{});
ser.ext(baseData11, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{};
std::shared_ptr<Base> baseRes2{new Derived{3, 78}};
std::weak_ptr<Base> baseRes3{baseRes2};
std::weak_ptr<Base> baseRes11{};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
des.ext(baseRes2, StdSharedPtr{});
des.ext(baseRes3, StdSharedPtr{});
des.ext(baseRes11, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
des.ext(baseRes2, StdSmartPtr{});
des.ext(baseRes3, StdSmartPtr{});
des.ext(baseRes11, StdSmartPtr{});
clearSharedState();
@@ -599,58 +591,82 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::shared_ptr<Base> baseRes1{new Base{}};
auto& des = createDeserializer();
des.ext(baseRes1, StdSharedPtr{});
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(1));
EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
EXPECT_THAT(dynamic_cast<Derived*>(baseRes1.get()), ::testing::NotNull());
EXPECT_THAT(dynamic_cast<Derived *>(baseRes1.get()), ::testing::NotNull());
EXPECT_TRUE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid) {
std::shared_ptr<Base> tmp{new Derived{3, 78}};
std::weak_ptr<Base> baseData1{tmp};
auto& ser = createSerializer();
ser.ext(baseData1, StdSharedPtr{});
EXPECT_FALSE(isPointerContextValid());
//
// std::weak_ptr<Base> baseRes1{};
// auto& des = createDeserializer();
// des.ext(baseRes1, StdSharedPtr{});
//
// EXPECT_TRUE(isPointerContextValid());
// clearSharedState();
//
// EXPECT_THAT(baseRes1.use_count(), Eq(1));
// EXPECT_THAT(baseRes1->x, Eq(baseData1->x));
// EXPECT_THAT(dynamic_cast<Derived*>(baseRes1.get()), ::testing::NotNull());
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
}
EXPECT_FALSE(isPointerContextValid());
}
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
auto &ser = createSerializer();
ser.ext(baseData1, StdSmartPtr{});
std::weak_ptr<Base> baseRes1{};
auto &des = createDeserializer();
des.ext(baseRes1, StdSmartPtr{});
EXPECT_FALSE(isPointerContextValid());
EXPECT_THAT(baseRes1.use_count(), Eq(1));
clearSharedState();
EXPECT_THAT(baseRes1.use_count(), Eq(0));
}
struct TestSharedFromThis : public std::enable_shared_from_this<TestSharedFromThis> {
float x{};
template<typename S>
void serialize(S &s) {
s.value4b(x);
}
};
TEST_F(SerializeExtensionStdSmartSharedPtr, EnableSharedFromThis) {
std::shared_ptr<TestSharedFromThis> dataPtr(new TestSharedFromThis{});
std::shared_ptr<TestSharedFromThis> resPtr{};
createSerializer().ext(dataPtr, StdSmartPtr{});
createDeserializer().ext(resPtr, StdSmartPtr{});
clearSharedState();
auto resPtr2 = resPtr->shared_from_this();
EXPECT_THAT(resPtr->x, Eq(dataPtr->x));
EXPECT_THAT(resPtr2.use_count(), Eq(2));
}