mirror of
https://github.com/fraillt/bitsery.git
synced 2026-09-08 03:18:29 +00:00
polymorphism improvements and new CompactValue extension
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 4
|
||||
#define BITSERY_MINOR_VERSION 3
|
||||
#define BITSERY_MINOR_VERSION 4
|
||||
#define BITSERY_PATCH_VERSION 0
|
||||
|
||||
#define BITSERY_QUOTE_MACRO(name) #name
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
|
||||
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
|
||||
};
|
||||
|
||||
//add swap functions to class, to avoid compilation warning about unused functions
|
||||
|
||||
176
include/bitsery/ext/compact_value.h
Normal file
176
include/bitsery/ext/compact_value.h
Normal file
@@ -0,0 +1,176 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2018 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_COMPACT_VALUE_H
|
||||
#define BITSERY_EXT_COMPACT_VALUE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
template <bool CheckOverflow>
|
||||
class CompactValueImpl {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &writer, const T &v, Fnc &&) const {
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
|
||||
using TValue = typename IntegralFromFundamental<T>::TValue;
|
||||
serializeImpl(s, writer, reinterpret_cast<const TValue&>(v), std::integral_constant<bool, sizeof(T) != 1>{});
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &reader, T &v, Fnc &&) const {
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
|
||||
using TValue = typename IntegralFromFundamental<T>::TValue;
|
||||
deserializeImpl(d, reader, reinterpret_cast<TValue &>(v), std::integral_constant<bool, sizeof(T) != 1>{});
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// if value is 1byte size, just serialize/ deserialize whole value
|
||||
template<typename Ser, typename Writer, typename T>
|
||||
void serializeImpl(Ser &s, Writer &, const T &v, std::false_type) const {
|
||||
s.value1b(v);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T>
|
||||
void deserializeImpl(Des &d, Reader &, T &v, std::false_type) const {
|
||||
d.value1b(v);
|
||||
}
|
||||
|
||||
// when value is bigger than 1byte size,
|
||||
template<typename Ser, typename Writer, typename T>
|
||||
void serializeImpl(Ser &, Writer &writer, const T &v, std::true_type) const {
|
||||
auto val = zigZagEncode(v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
|
||||
writeBytes(writer, val);
|
||||
}
|
||||
|
||||
|
||||
template<typename Des, typename Reader, typename T>
|
||||
void deserializeImpl(Des &, Reader &reader, T &v, std::true_type) const {
|
||||
using TUnsigned = SameSizeUnsigned<T>;
|
||||
TUnsigned res{};
|
||||
readBytes(reader, res);
|
||||
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
|
||||
}
|
||||
|
||||
// zigzag encode signed types
|
||||
template<typename T>
|
||||
const SameSizeUnsigned<T> &zigZagEncode(const T &v, std::false_type) const {
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename TResult, typename TUnsigned>
|
||||
const TResult &zigZagDecode(const TUnsigned &v, std::false_type) const{
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
|
||||
return (v << 1) ^ (v >> (BitsSize<T>::value - 1));
|
||||
}
|
||||
|
||||
template<typename TResult, typename TUnsigned>
|
||||
TResult zigZagDecode(TUnsigned v, std::true_type) const {
|
||||
return (v >> 1) ^ -(v & 1);
|
||||
}
|
||||
|
||||
// write/read bytes one by one
|
||||
template<typename Writer, typename T>
|
||||
void writeBytes(Writer &w, const T &v) const {
|
||||
auto val = v;
|
||||
while(val > 0x7Fu) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val | 0x80u));
|
||||
val >>=7u;
|
||||
}
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val));
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
void readBytes(Reader &r, T &v) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
uint8_t b1{0x80u};
|
||||
auto i = 0u;
|
||||
for (;i < TBITS && b1 > 0x7Fu; i +=7u) {
|
||||
r.template readBytes<1>(b1);
|
||||
v += static_cast<T>(b1 & 0x7Fu) << i;
|
||||
}
|
||||
checkReadOverflow<Reader, T>(r, i, b1, std::integral_constant<bool, CheckOverflow>{});
|
||||
}
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
|
||||
r.setError(bitsery::ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace ext {
|
||||
|
||||
// this type will use value overload, and do not check if type is sufficiently large during deserialization
|
||||
class CompactValue: public details::CompactValueImpl<false> {};
|
||||
|
||||
// this type will enable object overload, and set DataOverflow if value doesn't fit in type, during deserialization
|
||||
class CompactValueAsObject: public details::CompactValueImpl<true> {};
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::CompactValue, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
// disable object overload, because we don't have implemented serialization function for fundamental types
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::CompactValueAsObject, T> {
|
||||
// use dummy implemenations for value and object overload
|
||||
using TValue = void;
|
||||
// only enable object overload
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_COMPACT_VALUE_H
|
||||
@@ -70,6 +70,24 @@ namespace bitsery {
|
||||
isSharedProcessed{false} {};
|
||||
PointerOwnershipType ownershipType;
|
||||
bool isSharedProcessed;
|
||||
|
||||
void update(PointerOwnershipType ptrType) {
|
||||
//do nothing for observer
|
||||
if (ptrType == PointerOwnershipType::Observer)
|
||||
return;
|
||||
if (ownershipType == PointerOwnershipType::Observer) {
|
||||
//set ownership type
|
||||
ownershipType = ptrType;
|
||||
return;
|
||||
}
|
||||
//only shared ownership can get here multiple times
|
||||
assert(ptrType == PointerOwnershipType::SharedOwner || ptrType == PointerOwnershipType::SharedObserver);
|
||||
//check if need to update to SharedOwner
|
||||
if (ptrType == PointerOwnershipType::SharedOwner)
|
||||
ownershipType = ptrType;
|
||||
//mark that object already processed, so we do not serialize/deserialize duplicate objects
|
||||
isSharedProcessed = true;
|
||||
}
|
||||
};
|
||||
|
||||
struct PLCInfoSerializer: PLCInfo {
|
||||
@@ -111,24 +129,6 @@ namespace bitsery {
|
||||
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()
|
||||
@@ -152,7 +152,7 @@ namespace bitsery {
|
||||
++_currId;
|
||||
return ptrInfo;
|
||||
}
|
||||
updatePLCInfo(ptrInfo, ptrType);
|
||||
ptrInfo.update(ptrType);
|
||||
return ptrInfo;
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace bitsery {
|
||||
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType});
|
||||
auto &ptrInfo = res.first->second;
|
||||
if (!res.second)
|
||||
updatePLCInfo(ptrInfo, ptrType);
|
||||
ptrInfo.update(ptrType);
|
||||
return ptrInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,14 @@ namespace bitsery {
|
||||
// although you can add all derivates to same base like this:
|
||||
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
|
||||
// it will not work when you try to serialize Dog*, because it will not find Bulldog and GoldenRetriever
|
||||
template<typename TBase>
|
||||
struct PolymorphicBaseClass {
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
namespace {
|
||||
// this class must be in anonymous namespace, so that it would generate different symbols when defining different hierarchies in different translation units
|
||||
// https://github.com/fraillt/bitsery/issues/9
|
||||
template<typename TBase>
|
||||
struct PolymorphicBaseClass {
|
||||
using Childs = PolymorphicClassesList<>;
|
||||
};
|
||||
}
|
||||
|
||||
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
|
||||
//e.g.
|
||||
@@ -108,23 +112,23 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
|
||||
void add() {
|
||||
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, TBase, TDerived>(typename PolymorphicBaseClass<TDerived>::Childs{});
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(typename THierarchy<TDerived>::Childs{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived, typename T1, typename ... Tn>
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived, typename T1, typename ... Tn>
|
||||
void addChilds(PolymorphicClassesList<T1, Tn...>) {
|
||||
static_assert(std::is_base_of<TDerived, T1>::value,
|
||||
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
|
||||
add<TSerializer, TBase, T1>();
|
||||
addChilds<TSerializer, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
|
||||
add<TSerializer, THierarchy, TBase, T1>();
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
|
||||
//iterate through derived class hierarchy as well
|
||||
add<TSerializer, T1, T1>();
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
|
||||
void addChilds(PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
@@ -154,16 +158,39 @@ namespace bitsery {
|
||||
_baseToDerivedArray.clear();
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename T1, typename ...Tn>
|
||||
void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, T1, T1>();
|
||||
registerBasesList<TSerializer>(s, PolymorphicClassesList<Tn...>{});
|
||||
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
|
||||
[[deprecated("de/serializer instance is not required")]] void registerBasesList(const TSerializer &s, PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(s, PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer>
|
||||
void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
[[deprecated]] void registerBasesList(const TSerializer &, PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
// THierarchy is the name of class, that defines hierarchy
|
||||
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
|
||||
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
|
||||
// https://github.com/fraillt/bitsery/issues/9
|
||||
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
|
||||
void registerBasesList(PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
void registerBasesList(PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
// optional method, in case you want to construct base class hierarchy your self
|
||||
template <typename TSerializer, typename TBase, typename TDerived>
|
||||
void registerSingleBaseBranch() {
|
||||
static_assert(std::is_base_of<TBase, TDerived>::value, "TDerived must be derived from TBase");
|
||||
static_assert(!std::is_abstract<TDerived>::value, "TDerived cannot be abstract");
|
||||
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
|
||||
};
|
||||
|
||||
|
||||
template<typename Serializer, typename Writer, typename TBase>
|
||||
void serialize(Serializer &ser, Writer &writer, TBase &obj) {
|
||||
//get derived key
|
||||
|
||||
Reference in New Issue
Block a user