mirror of
https://github.com/fraillt/bitsery.git
synced 2026-09-13 13:58:18 +00:00
polymorphism and smart pointers
This commit is contained in:
26
CHANGELOG.md
26
CHANGELOG.md
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>(), "");
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
include/bitsery/flexible/memory.h
Normal file
45
include/bitsery/flexible/memory.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>>;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user