From f6d02aba38c2f4fcf7f319ccf77d577271580b72 Mon Sep 17 00:00:00 2001 From: fraillt Date: Mon, 6 Nov 2017 11:30:14 +0200 Subject: [PATCH] added inheritance extension, and ability to have internal contexts within serializer/deserializer --- CHANGELOG.md | 27 +- doc/README.md | 8 +- examples/inheritance.cpp | 117 ++++++ examples/raw_pointers.cpp | 5 +- include/bitsery/adapter/buffer.h | 6 +- include/bitsery/adapter/stream.h | 6 +- include/bitsery/adapter_reader.h | 9 +- include/bitsery/adapter_writer.h | 16 +- include/bitsery/bitsery.h | 2 +- include/bitsery/common.h | 7 +- include/bitsery/deserializer.h | 14 +- include/bitsery/details/adapter_common.h | 6 +- include/bitsery/details/adapter_utils.h | 6 +- include/bitsery/details/not_defined_type.h | 6 +- .../bitsery/details/serialization_common.h | 363 +++++++++++------- include/bitsery/details/sessions.h | 6 +- include/bitsery/ext/growable.h | 2 + include/bitsery/ext/inheritance.h | 156 ++++++++ include/bitsery/ext/pointer.h | 282 +++++++------- include/bitsery/ext/std_map.h | 1 + include/bitsery/ext/std_optional.h | 1 + include/bitsery/ext/std_set.h | 1 + include/bitsery/serializer.h | 14 +- include/bitsery/traits/core/std_defaults.h | 6 +- tests/serialization_context.cpp | 64 +++ tests/serialization_ext_inheritance.cpp | 354 +++++++++++++++++ tests/serialization_ext_pointer.cpp | 6 +- 27 files changed, 1179 insertions(+), 312 deletions(-) create mode 100644 examples/inheritance.cpp create mode 100644 include/bitsery/ext/inheritance.h create mode 100644 tests/serialization_ext_inheritance.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 8186988..39cacee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# [4.2.0](https://github.com/fraillt/bitsery/compare/v4.1.0...v4.2.0) (2017-11-12) + +### Features + +* serializer/deserializer can now have **internal context(s)** via configuration. +It is convenient way to pass context, when it doesn't convey useful information outside of serializer/deserializer and is default constructable. +* added **contextOrNull\()** overload to *BasicSerializer/BasicDeserializer*. +Difference between *contextOrNull\()* and *context\()* is, that using *context\()* code doesn't compile if T doesn't exists at all, while using *contextOrNull\()* code compiles, but returns *nullptr* at runtime. +* added inheritance support via extensions. +In order to correctly manage virtual inheritance two extensions was created in **** header: + * **BaseClass\** - use when inheriting from objects without virtual inheritance. + * **VirtualBaseClass\** - ensures that only one copy of each virtual base class is serialized. + + To keep track of virtual base classes **InheritanceContext** is required, but it is optional if no virtual bases exists in serialization flow. + I.e. if context is not defined, code will not compile only if virtual inheritance is used. + See [inheritance](examples/inheritance.cpp) for usage example. + +### Improvements +* added optional ctor parameter for *PointerOwner* and *PointerObserver* - **PointerType**, which specifies if pointer can be null or not. +Default is **Nullable**. + # [4.1.0](https://github.com/fraillt/bitsery/compare/v4.0.1...v4.1.0) (2017-10-27) ### Features @@ -12,9 +33,9 @@ In order to correctly manage pointer ownership, three extensions was created in *Currently polimorphism and std::shared_ptr, std::unique_ptr is not supported.* -* added **context\()** overload to *BasicSerializer/BasicDeserializer* and now serializers is typesafe. -Also for better pointers support, added posibility to have multiple types in context with *std::tuple*. -E.g. when using pointers together with your custom context, you can define your context as *std::tuple\* and in serialization function you can correctly get your data via *context\()*. +* added **context\()** overload to *BasicSerializer/BasicDeserializer* and now they became typesafe. +For better extensions support, added posibility to have multiple types in context with *std::tuple*. +E.g. when using multiple extensions, that requires specific contexts, together with your custom context, you can define your context as *std::tuple\* and in serialization function you can correctly get your data via *context\()*. ### Improvements diff --git a/doc/README.md b/doc/README.md index 28acc25..9821b0d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -10,6 +10,7 @@ Library design: * `errors handling` * `forward/backward compatibility via Growable extension` * `pointers` +* `inheritance` Core Serializer/Deserializer functions (alphabetical order): @@ -18,11 +19,14 @@ Core Serializer/Deserializer functions (alphabetical order): * `container` * `ext` * `context` +* `context` +* `contextOrNull` * `object` * `text` * `value` Serializer/Deserializer extensions via `ext` method (alphabetical order): +* `BaseClass` * `Entropy` * `Growable` * `PointerOwner` @@ -34,6 +38,7 @@ Serializer/Deserializer extensions via `ext` method (alphabetical order): * `StdSet` * `StdStack` * `ValueRange` +* `VirtualBaseClass` AdapterWriter/Reader functions: * `writeBits/readBits` @@ -62,9 +67,6 @@ 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. -Limitations: -* max **text** or **container** size can be 2^(n-2) (where n = sizeof(std::size_t) * 8) for 32-bit systems it is 1073741823 (0x3FFFFFF). - Other: * [Contributing](../CONTRIBUTING.md) * [Change log](../CHANGELOG.md) diff --git a/examples/inheritance.cpp b/examples/inheritance.cpp new file mode 100644 index 0000000..7e66cf7 --- /dev/null +++ b/examples/inheritance.cpp @@ -0,0 +1,117 @@ +// +//this example coverls all the corner cases that can happen using inherintace +//in reality virtual inherintance is usually avoided, so your code would look much simpler. +// + +#include +#include +#include + +//include inheritance extension +//this header contains two extensions, that specifies inheritance type of base class +// BaseClass - normal inheritance +// VirtualBaseClass - when virtual inheritance is used +//in order for virtual inheritance to work, InheritanceContext is required. +//it can be created either internally (via configuration) or externally (pointer to context). +#include + +using bitsery::ext::BaseClass; +using bitsery::ext::VirtualBaseClass; + +struct Base { + uint8_t x{}; + //Base doesn't have to be polymorphic class, inheritance works at compile-time. +}; +template +void serialize(S& s, Base& o) { + s.value1b(o.x); +} + +struct Derive1:virtual Base {// virtually inherits from base + uint8_t y1{}; +}; +template +void serialize(S& s, Derive1& o) { + //define virtual inheritance, it will not compile if InheritanceContext is not defined in serializer/deserialzer + s.ext(o, VirtualBaseClass{}); + s.value1b(o.y1); +} + +//to make it more interesting, serialize private member +struct Derived2:virtual Base { + explicit Derived2(uint8_t y):y2{y} {} + + uint8_t getY2() const { + return y2; + }; +private: + friend bitsery::Access; + uint8_t y2{}; + template + void serialize(S& s) { + //notice virtual inheritance + s.ext(*this, VirtualBaseClass{}); + s.value1b(y2); + } +}; + +struct MultipleInheritance: Derive1, Derived2 { + explicit MultipleInheritance(uint8_t y2):Derived2{y2} {} + uint8_t z{}; +}; +template +void serialize(S& s, MultipleInheritance& o) { + //has two bases, serialize them separately + s.ext(o, BaseClass{}); + s.ext(o, BaseClass{}); + s.value1b(o.z); +} + +namespace bitsery { + // call to serialize function with Derived2 and MultipleInheritance is ambiguous, + // it matches two serialize functions: Base classes non-member fnc and Derived2 member fnc + // we need explicitly select which function to use + template <> + struct SelectSerializeFnc:UseMemberFnc {}; + + //multiple inheritance has non-member serialize function defined + template <> + struct SelectSerializeFnc:UseNonMemberFnc {}; +} + +using namespace bitsery; + +// since in this example we're using virtual inheritance we need InheritanceContext as well. +// InheritanceContext is default constructable and has no useful information outside of serializer/deserializer +// lets create it internally, via configuration +struct ConfigWithContext:DefaultConfig { + //always add internal contexts to tuple + using InternalContext = std::tuple; +}; + +//some helper types +using Buffer = std::vector; +using Writer = AdapterWriter, ConfigWithContext>; +using Reader = AdapterReader, ConfigWithContext>; + +int main() { + + MultipleInheritance data{98}; + data.x = 254; + data.y1 = 47; + data.z = 1; + + Buffer buf{}; + + BasicSerializer ser{OutputBufferAdapter{buf}, nullptr}; + ser.object(data); + auto writtenSize = AdapterAccess::getWriter(ser).writtenBytesCount(); + + MultipleInheritance res{0}; + BasicDeserializer des{InputBufferAdapter{buf.begin(), writtenSize}}; + des.object(res); + assert(AdapterAccess::getReader(des).error() == ReaderError::NoError && AdapterAccess::getReader(des).isCompletedSuccessfully()); + + assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z); + assert(writtenSize == 4);//base is serialized once, because it is inherited virtually +} diff --git a/examples/raw_pointers.cpp b/examples/raw_pointers.cpp index 481d345..e4ca744 100644 --- a/examples/raw_pointers.cpp +++ b/examples/raw_pointers.cpp @@ -11,6 +11,7 @@ using bitsery::ext::ReferencedByPointer; using bitsery::ext::PointerObserver; using bitsery::ext::PointerOwner; +using bitsery::ext::PointerType ; enum class MyEnum:uint16_t { V1,V2,V3 }; struct MyStruct { @@ -71,8 +72,8 @@ struct Test1Data { }); //observer s.ext(po1, PointerObserver{}); - //owner - s.ext4b(pi1, PointerOwner{}); + //owner, mark it as not null + s.ext4b(pi1, PointerOwner{PointerType::NotNull}); } }; diff --git a/include/bitsery/adapter/buffer.h b/include/bitsery/adapter/buffer.h index 1de7045..f75531c 100644 --- a/include/bitsery/adapter/buffer.h +++ b/include/bitsery/adapter/buffer.h @@ -21,8 +21,8 @@ //SOFTWARE. -#ifndef BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H -#define BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H +#ifndef BITSERY_ADAPTER_BUFFER_H +#define BITSERY_ADAPTER_BUFFER_H #include "../details/adapter_common.h" #include "../traits/core/traits.h" @@ -204,4 +204,4 @@ namespace bitsery { } -#endif //BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H +#endif //BITSERY_ADAPTER_BUFFER_H diff --git a/include/bitsery/adapter/stream.h b/include/bitsery/adapter/stream.h index e765cfc..eadf821 100644 --- a/include/bitsery/adapter/stream.h +++ b/include/bitsery/adapter/stream.h @@ -21,8 +21,8 @@ //SOFTWARE. -#ifndef BITSERY_ADAPTERS_DYNAMIC_STREAM_H -#define BITSERY_ADAPTERS_DYNAMIC_STREAM_H +#ifndef BITSERY_ADAPTER_STREAM_H +#define BITSERY_ADAPTER_STREAM_H #include "../details/adapter_common.h" #include "../traits/array.h" @@ -224,4 +224,4 @@ namespace bitsery { using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter>; } -#endif //BITSERY_ADAPTERS_DYNAMIC_STREAM_H +#endif //BITSERY_ADAPTER_STREAM_H diff --git a/include/bitsery/adapter_reader.h b/include/bitsery/adapter_reader.h index a1b9aa8..ee05052 100644 --- a/include/bitsery/adapter_reader.h +++ b/include/bitsery/adapter_reader.h @@ -22,8 +22,8 @@ -#ifndef BITSERY_BASIC_READER_H -#define BITSERY_BASIC_READER_H +#ifndef BITSERY_ADAPTER_READER_H +#define BITSERY_ADAPTER_READER_H #include "details/sessions.h" #include @@ -39,7 +39,7 @@ namespace bitsery { struct AdapterReader { //this is required by deserializer static constexpr bool BitPackingEnabled = false; - + using TConfig = Config; using TValue = typename InputAdapter::TValue; static_assert(details::IsDefined::value, "Please define adapter traits or include from "); @@ -154,6 +154,7 @@ namespace bitsery { public: //this is required by deserializer static constexpr bool BitPackingEnabled = true; + using TConfig = typename TReader::TConfig; //make TValue unsigned for bitpacking using UnsignedValue = typename std::make_unsigned::type; using ScratchType = typename details::ScratchType::type; @@ -268,4 +269,4 @@ namespace bitsery { }; } -#endif //BITSERY_BUFFER_READER_H +#endif //BITSERY_ADAPTER_READER_H diff --git a/include/bitsery/adapter_writer.h b/include/bitsery/adapter_writer.h index d5ed7e7..e0b83d0 100644 --- a/include/bitsery/adapter_writer.h +++ b/include/bitsery/adapter_writer.h @@ -22,8 +22,8 @@ -#ifndef BITSERY_BASIC_WRITER_H -#define BITSERY_BASIC_WRITER_H +#ifndef BITSERY_ADAPTER_WRITER_H +#define BITSERY_ADAPTER_WRITER_H #include "details/sessions.h" @@ -33,10 +33,12 @@ namespace bitsery { - struct MeasureSize { + template + struct BasicMeasureSize { //measure class is bit-packing enabled, no need to create wrapper for it static constexpr bool BitPackingEnabled = true; + using TConfig = Config; template void writeBytes(const T &) { static_assert(std::is_integral(), ""); @@ -95,6 +97,9 @@ namespace bitsery { size_t _sessionsBytesCount{}; }; + //helper type for default config + using MeasureSize = BasicMeasureSize; + template class AdapterWriterBitPackingWrapper; @@ -103,7 +108,7 @@ namespace bitsery { struct AdapterWriter { //this is required by serializer static constexpr bool BitPackingEnabled = false; - + using TConfig = Config; using TValue = typename OutputAdapter::TValue; static_assert(details::IsDefined::value, "Please define adapter traits or include from "); @@ -207,6 +212,7 @@ namespace bitsery { public: //this is required by serializer static constexpr bool BitPackingEnabled = true; + using TConfig = typename TWriter::TConfig; //make TValue unsigned for bit packing using UnsignedType = typename std::make_unsigned::type; @@ -334,4 +340,4 @@ namespace bitsery { }; } -#endif //BITSERY_BASIC_WRITER_H +#endif //BITSERY_ADAPTER_WRITER_H diff --git a/include/bitsery/bitsery.h b/include/bitsery/bitsery.h index dd81808..40147b3 100644 --- a/include/bitsery/bitsery.h +++ b/include/bitsery/bitsery.h @@ -25,7 +25,7 @@ #define BITSERY_BITSERY_H #define BITSERY_MAJOR_VERSION 4 -#define BITSERY_MINOR_VERSION 1 +#define BITSERY_MINOR_VERSION 2 #define BITSERY_PATCH_VERSION 0 #define BITSERY_QUOTE_MACRO(name) #name diff --git a/include/bitsery/common.h b/include/bitsery/common.h index 73c40b0..27c439e 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -24,6 +24,8 @@ #ifndef BITSERY_COMMON_H #define BITSERY_COMMON_H +#include + namespace bitsery { /* @@ -41,7 +43,10 @@ namespace bitsery { //this functionality allows to support backward/forward compatibility //however reading from streams is not supported, because this functionality requires random access to buffer. static constexpr bool BufferSessionsEnabled = false; - + //list of contexts that will be instanciated internally within serializer/deserializer. + //contexts must be default constructable. + //internal context has priority, if external context with the same type exists. + using InternalContext = std::tuple<>; }; } diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index fe52a1a..76c56d7 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -40,11 +40,14 @@ namespace bitsery { using BPEnabledType = BasicDeserializer>::type, TContext>; + static_assert(details::IsSpecializationOf::value, + "Config::InternalContext must be std::tuple"); template explicit BasicDeserializer(ReaderParam&& r, TContext* context = nullptr) : _reader{std::forward(r)}, - _context{context} + _context{context}, + _internalContext{} { } @@ -66,7 +69,12 @@ namespace bitsery { template T* context(){ - return details::getContext(_context); + return details::getContext(_context, _internalContext); + } + + template + T* contextOrNull(){ + return details::getContextIfTypeExists(_context, _internalContext); } /* @@ -326,6 +334,8 @@ namespace bitsery { TAdapterReader _reader; TContext* _context; + typename TReader::TConfig::InternalContext _internalContext; + //process value types //false_type means that we must process all elements individually diff --git a/include/bitsery/details/adapter_common.h b/include/bitsery/details/adapter_common.h index b7bb029..00146e4 100644 --- a/include/bitsery/details/adapter_common.h +++ b/include/bitsery/details/adapter_common.h @@ -20,8 +20,8 @@ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. -#ifndef BITSERY_DETAILS_BUFFER_COMMON_H -#define BITSERY_DETAILS_BUFFER_COMMON_H +#ifndef BITSERY_DETAILS_ADAPTER_COMMON_H +#define BITSERY_DETAILS_ADAPTER_COMMON_H #include #include @@ -124,4 +124,4 @@ namespace bitsery { } -#endif //BITSERY_DETAILS_BUFFER_COMMON_H +#endif //BITSERY_DETAILS_ADAPTER_COMMON_H diff --git a/include/bitsery/details/adapter_utils.h b/include/bitsery/details/adapter_utils.h index f3d9637..611c576 100644 --- a/include/bitsery/details/adapter_utils.h +++ b/include/bitsery/details/adapter_utils.h @@ -20,8 +20,8 @@ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. -#ifndef BITSERY_DETAILS_BOTH_COMMON_H -#define BITSERY_DETAILS_BOTH_COMMON_H +#ifndef BITSERY_DETAILS_ADAPTER_UTILS_H +#define BITSERY_DETAILS_ADAPTER_UTILS_H #include #include @@ -84,4 +84,4 @@ namespace bitsery { } } -#endif //BITSERY_DETAILS_BOTH_COMMON_H +#endif //BITSERY_DETAILS_ADAPTER_UTILS_H diff --git a/include/bitsery/details/not_defined_type.h b/include/bitsery/details/not_defined_type.h index e280661..f91bb87 100644 --- a/include/bitsery/details/not_defined_type.h +++ b/include/bitsery/details/not_defined_type.h @@ -21,8 +21,8 @@ //SOFTWARE. -#ifndef BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H -#define BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H +#ifndef BITSERY_DETAILS_NOT_DEFINED_TYPE_H +#define BITSERY_DETAILS_NOT_DEFINED_TYPE_H #include @@ -76,4 +76,4 @@ namespace std { using iterator_category = std::random_access_iterator_tag; }; } -#endif //BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H +#endif //BITSERY_DETAILS_NOT_DEFINED_TYPE_H diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h index 1e553aa..d53623e 100644 --- a/include/bitsery/details/serialization_common.h +++ b/include/bitsery/details/serialization_common.h @@ -41,18 +41,33 @@ namespace bitsery { } }; + //when call to serialize function is ambiguous (member and non-member serialize function exists for a type) + //specialize this class, by inheriting from either UseNonMemberFnc or UseMemberFnc + //e.g. + //template <> struct SelectSerializeFnc:UseMemberFnc {}; + template + struct SelectSerializeFnc : std::integral_constant { + }; + + //types you need to inherit from when specializing SelectSerializeFnc class + struct UseNonMemberFnc : std::integral_constant { + }; + struct UseMemberFnc : std::integral_constant { + }; + + //serializer/deserializer, does not public interface to get underlying writer/reader //to prevent users from using writer/reader directly, because they have different interface //and they cannot be used describing serialization flows.: use extensions for this reason. //this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions. struct AdapterAccess { - template - static typename Serializer::TWriter& getWriter(Serializer& s) { + template + static typename Serializer::TWriter &getWriter(Serializer &s) { return s._writer; } - template - static typename Deserializer::TReader& getReader(Deserializer& s) { + template + static typename Deserializer::TReader &getReader(Deserializer &s) { return s._reader; } }; @@ -60,100 +75,103 @@ namespace bitsery { namespace details { //helper types for error handling - template - struct IsContainerTraitsDefined:public IsDefined::TValue> { + template + struct IsContainerTraitsDefined : public IsDefined::TValue> { }; - template - struct IsTextTraitsDefined:public IsDefined::TValue> { + template + struct IsTextTraitsDefined : public IsDefined::TValue> { }; - template - struct IsExtensionTraitsDefined:public IsDefined::TValue> { + template + struct IsExtensionTraitsDefined : public IsDefined::TValue> { }; #ifdef _MSC_VER - //helper types for HasSerializeFunction - template - using TrySerializeFunction = decltype(serialize(std::declval(), std::declval())); - - template - struct HasSerializeFunctionHelper { - template > - static std::true_type tester(Q&&, R&&); - static std::false_type tester(...); - using type = decltype(tester(std::declval(), std::declval())); - }; - template - struct HasSerializeFunction :HasSerializeFunctionHelper::type {}; - - //helper types for HasSerializeMethod - template - using TrySerializeMethod = decltype(Access::serialize(std::declval(), std::declval())); - - template - struct HasSerializeMethodHelper { - template > - static std::true_type tester(Q&&, R&&); - static std::false_type tester(...); - using type = decltype(tester(std::declval(), std::declval())); - }; - template - struct HasSerializeMethod :HasSerializeMethodHelper::type {}; - - //helper types for IsFlexibleIncluded - template - using TryArchiveProcess = decltype(archiveProcess(std::declval(), std::declval())); - - template - struct IsFlexibleIncludedHelper { - template > - static std::true_type tester(Q&&, R&&); - static std::false_type tester(...); - using type = decltype(tester(std::declval(), std::declval())); - }; - - template - struct IsFlexibleIncluded :IsFlexibleIncludedHelper::type {}; + //helper types for HasSerializeFunction + template + using TrySerializeFunction = decltype(serialize(std::declval(), std::declval())); + + template + struct HasSerializeFunctionHelper { + template > + static std::true_type tester(Q&&, R&&); + static std::false_type tester(...); + using type = decltype(tester(std::declval(), std::declval())); + }; + template + struct HasSerializeFunction :HasSerializeFunctionHelper::type {}; + + //helper types for HasSerializeMethod + template + using TrySerializeMethod = decltype(Access::serialize(std::declval(), std::declval())); + + template + struct HasSerializeMethodHelper { + template > + static std::true_type tester(Q&&, R&&); + static std::false_type tester(...); + using type = decltype(tester(std::declval(), std::declval())); + }; + template + struct HasSerializeMethod :HasSerializeMethodHelper::type {}; + + //helper types for IsFlexibleIncluded + template + using TryArchiveProcess = decltype(archiveProcess(std::declval(), std::declval())); + + template + struct IsFlexibleIncludedHelper { + template > + static std::true_type tester(Q&&, R&&); + static std::false_type tester(...); + using type = decltype(tester(std::declval(), std::declval())); + }; + + template + struct IsFlexibleIncluded :IsFlexibleIncludedHelper::type {}; #else - //helper metafunction, that is added to c++17 - template - struct make_void { - typedef void type; - }; - template - using void_t = typename make_void::type; - - template - struct HasSerializeFunction :std::false_type {}; - - template - struct HasSerializeFunction(), std::declval()))> - > : std::true_type {}; - - - template - struct HasSerializeMethod :std::false_type {}; - - template - struct HasSerializeMethod(), std::declval()))> - > : std::true_type {}; - - //this solution doesn't work with visual studio, but is more elegant - template - struct IsFlexibleIncluded :std::false_type {}; - - template - struct IsFlexibleIncluded(), std::declval()))> - > : std::true_type {}; -#endif + //helper metafunction, that is added to c++17 + template + struct make_void { + typedef void type; + }; + template + using void_t = typename make_void::type; + template + struct HasSerializeFunction : std::false_type { + }; + template + struct HasSerializeFunction(), std::declval()))> + > : std::true_type { + }; + template + struct HasSerializeMethod : std::false_type { + }; + + template + struct HasSerializeMethod(), std::declval()))> + > : std::true_type { + }; + + //this solution doesn't work with visual studio, but is more elegant + template + struct IsFlexibleIncluded : std::false_type { + }; + + template + struct IsFlexibleIncluded(), std::declval()))> + > : std::true_type { + }; +#endif + //used for extensions, when extension TValue = void struct DummyType { @@ -201,23 +219,34 @@ namespace bitsery { struct SerializeFunction { static void invoke(S &s, T &v) { - static_assert(HasSerializeFunction::value || HasSerializeMethod::value, + static_assert(HasSerializeFunction::value || HasSerializeMethod::value, "\nPlease define 'serialize' function for your type (inside or outside of class):\n" " template\n" " void serialize(S& s)\n" " {\n" " ...\n" " }\n"); - static_assert(!(HasSerializeFunction::value && HasSerializeMethod::value), - "\nPlease define only one 'serialize' function (member OR free), not both."); - internalInvoke(s,v, HasSerializeMethod{}); + using TDecayed = typename std::decay::type; + selectSerializeFnc(s, v, SelectSerializeFnc{}); } + private: - static void internalInvoke(S& s, T& v,std::true_type) { - Access::serialize(s,v); + static void selectSerializeFnc(S &s, T &v, std::integral_constant) { + static_assert(!(HasSerializeFunction::value && HasSerializeMethod::value), + "\nPlease define only one 'serialize' function (member OR free).\n" + "If serialization function is inherited from base class, then explicitly select correct function for your type e.g.:\n" + " template <>\n" + " struct SelectSerializeFnc:UseMemberFnc {};\n"); + selectSerializeFnc(s, v, std::integral_constant::value ? 1 : 2>{}); } - static void internalInvoke(S& s, T& v,std::false_type) { - serialize(s,v); + + static void selectSerializeFnc(S &s, T &v, std::integral_constant) { + serialize(s, v); + } + + static void selectSerializeFnc(S &s, T &v, std::integral_constant) { + Access::serialize(s, v); } }; @@ -228,7 +257,7 @@ namespace bitsery { template struct ArchiveFunction { - static void invoke(S &s, T&& obj) { + static void invoke(S &s, T &&obj) { static_assert(IsFlexibleIncluded::value, "\nPlease include '' to use 'archive' function:\n"); @@ -237,71 +266,135 @@ namespace bitsery { }; /* - * function for getting context from serializer/deserializer - * has different behaviour when context is tuple + * helper function for getting context from serializer/deserializer */ - template class Template> - struct IsSpecializationOf : std::false_type - {}; + template class Template> + struct IsSpecializationOf : std::false_type { + }; - template