Compare commits

..

4 Commits

Author SHA1 Message Date
Martchus 2138491ee5 WIP 2021-07-10 23:28:02 +02:00
Martchus db0082c325 WIP: Versioning 2021-07-04 21:52:01 +02:00
Martchus 2c6c63579b WIP: Track version numbers 2021-07-03 22:26:50 +02:00
Martchus 2c243c3e41 WIP: Add marker macros for versioning 2021-07-03 22:26:50 +02:00
25 changed files with 139 additions and 427 deletions

19
.github/stale.yml vendored
View File

@ -1,19 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- feature request
- enhancement
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# set metadata # set metadata
project(reflective_rapidjson) project(reflective_rapidjson)

View File

@ -62,19 +62,18 @@ The following table shows the mapping of supported C++ types to supported JSON t
| iteratable lists (`std::vector`, `std::list`, ...) | array | | iteratable lists (`std::vector`, `std::list`, ...) | array |
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array | | sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array |
| `std::pair`, `std::tuple` | array | | `std::pair`, `std::tuple` | array |
| `std::unique_ptr`, `std::shared_ptr`, `std::optional` | depends/null | | `std::unique_ptr`, `std::shared_ptr` | depends/null |
| `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` | object | | `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` | object |
| `std::variant` | object | | `std::variant` | object |
| `JsonSerializable` | object | | `JsonSerializable` | object |
### Remarks ### Remarks
* Raw pointers are not supported. This prevents * Raw pointer are not supported. This prevents
forgetting to free memory which would have to be allocated when deserializing. forgetting to free memory which would have to be allocated when deserializing.
* For the same reason `const char *` and `std::string_view` are only supported for serialization. * For the same reason `const char *` and `std::string_view` are only supported for serialization.
* Enums are (de)serialized as their underlying integer value. When deserializing, it is currently *not* checked * Enums are (de)serialized as their underlying integer value. When deserializing, it is currently *not* checked
whether the present integer value is a valid enumeration item. whether the present integer value is a valid enumeration item.
* The JSON type for smart pointers and `std::optional` depends on the type the pointer/optional refers to. * The JSON type for smart pointer depends on the type the pointer refers to. It can also be `null`.
It can also be `null` for null pointers or `std::optional` without value.
* If multiple `std::shared_ptr` instances point to the same object this object is serialized multiple times. * If multiple `std::shared_ptr` instances point to the same object this object is serialized multiple times.
When deserializing those identical objects, it is currently not possible to share the memory (again). So each When deserializing those identical objects, it is currently not possible to share the memory (again). So each
`std::shared_ptr` will point to its own copy. Note that this limitation is *not* present when using binary `std::shared_ptr` will point to its own copy. Note that this limitation is *not* present when using binary
@ -354,54 +353,6 @@ An example for such custom (de)serialization can be found in the file
`json/reflector-chronoutilities.h`. It provides (de)serialization of `DateTime` and `json/reflector-chronoutilities.h`. It provides (de)serialization of `DateTime` and
`TimeSpan` objects from the C++ utilities library mentioned under dependencies. `TimeSpan` objects from the C++ utilities library mentioned under dependencies.
### Versioning
#### JSON (de)serializer
The JSON (de)serializer doesn't support versioning at this point. It'll simply read/write the
members present in the struct. Additional members (which were e.g. present in older/newer
versions of the struct) are ignored when reading and in consequence dropped when writing.
#### Binary (de)serializer
The binary (de)serializer supports *very* experimental versioning. Otherwise adding/removing
members is a breaking change. The versioning looks like this:
<pre>
// enable definition of the macros shown below (otherwise use long macros defined in
// `lib/versioning.h`)
#define REFLECTIVE_RAPIDJSON_SHORT_MACROS
#include &lt;reflective_rapidjson/binary/serializable.h&gt;
// example struct where version is *not* serialized/deserialized; defaults to version from
// outer scope when reading/writing, defaults to version 0 on top-level
struct Nested : public BinarySerializable&lt;Nested&gt; { //
std::uint32_t foo; // will be read/written in any case
as_of_version(3):
std::uint32_t bar; // will be read/written if outer scope version is &gt;= 3
};
// example struct where version is serialized/deserialized; defaults to version 3 when writing
struct Example : public BinarySerializable&lt;Example, 3&gt; {
Nested nested; // will be read/written in any case, version is "propagated down"
std::uint32_t a, b; // will be read/written in any case
until_version(2):
std::uint32_t c, d; // will be read/written if version is &lt;= 2
as_of_version(3):
std::uint32_t e, f; // will be read/written if version is &gt;= 3
as_of_version(4):
std::uint32_t g; // will be read/written if version is &gt;= 4
};
</pre>
The version specified as template argument is also assumed to be the highest supported version.
If a higher version is encountered during deserialization, `BinaryVersionNotSupported` is thrown
and the deserialization aborted.
Note that the versioning is mostly untested at this point.
### Remarks ### Remarks
* Static member variables and member functions are currently ignored by the generator. * Static member variables and member functions are currently ignored by the generator.
* It is currently not possible to ignore a specific member variable. * It is currently not possible to ignore a specific member variable.
@ -511,7 +462,7 @@ make
make check make check
# build tests but do not run them (optional, requires CppUnit) # build tests but do not run them (optional, requires CppUnit)
make tests make tests
# generate API documentation (optional, requires Doxygen) # generate API documentation (optional, reqquires Doxygen)
make apidoc make apidoc
# install header files, libraries and generator # install header files, libraries and generator
make install DESTDIR="/temporary/install/location" make install DESTDIR="/temporary/install/location"
@ -534,8 +485,3 @@ Add eg. `-j$(nproc)` to `make` arguments for using all cores.
These packages shows the required dependencies and commands to build in a plain way. So they might be useful for These packages shows the required dependencies and commands to build in a plain way. So they might be useful for
making Reflective RapidJSON available under other platforms, too. making Reflective RapidJSON available under other platforms, too.
## Copyright notice and license
Copyright © 2017-2023 Marius Kittler
All code is licensed under [GPL-2-or-later](LICENSE).

View File

@ -29,5 +29,5 @@
- [x] Support `std::unique_ptr` and `std::shared_ptr` - [x] Support `std::unique_ptr` and `std::shared_ptr`
- [x] Support `std::map` and `std::unordered_map` - [x] Support `std::map` and `std::unordered_map`
- [ ] Support `std::any` - [ ] Support `std::any`
- [x] Support `std::optional` - [ ] Support `std::optional`
- [x] Support/document customized (de)serialization (eg. serialize some `DateTime` object to ISO string representation) - [x] Support/document customized (de)serialization (eg. serialize some `DateTime` object to ISO string representation)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# metadata # metadata
set(META_PROJECT_NAME reflective_rapidjson_generator) set(META_PROJECT_NAME reflective_rapidjson_generator)
@ -60,9 +60,6 @@ endif ()
# also add reflective_rapidjson which is header-only but might pull additional include dirs for RapidJSON # also add reflective_rapidjson which is header-only but might pull additional include dirs for RapidJSON
list(APPEND PRIVATE_LIBRARIES "${REFLECTIVE_RAPIDJSON_TARGET_NAME}") list(APPEND PRIVATE_LIBRARIES "${REFLECTIVE_RAPIDJSON_TARGET_NAME}")
# avoid warning "'this' pointer is null" from GCC 12 about code included from libclang
list(APPEND META_PRIVATE_COMPILE_OPTIONS "-Wno-error=nonnull")
# include modules to apply configuration # include modules to apply configuration
include(BasicConfig) include(BasicConfig)
include(WindowsResources) include(WindowsResources)

View File

@ -246,10 +246,6 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
// print writeCustomType method // print writeCustomType method
os << "template <> " << visibility << " void writeCustomType<::" << relevantClass.qualifiedName os << "template <> " << visibility << " void writeCustomType<::" << relevantClass.qualifiedName
<< ">(BinarySerializer &serializer, const ::" << relevantClass.qualifiedName << " &customObject, BinaryVersion version)\n{\n"; << ">(BinarySerializer &serializer, const ::" << relevantClass.qualifiedName << " &customObject, BinaryVersion version)\n{\n";
os << " // write base classes\n";
for (const RelevantClass *baseClass : relevantBases) {
os << " serializer.write(static_cast<const ::" << baseClass->qualifiedName << " &>(customObject), version);\n";
}
if (!relevantClass.relevantBase.empty()) { if (!relevantClass.relevantBase.empty()) {
os << " // write version\n" os << " // write version\n"
" using V = Versioning<" " using V = Versioning<"
@ -257,8 +253,14 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
<< ">;\n" << ">;\n"
" if constexpr (V::enabled) {\n" " if constexpr (V::enabled) {\n"
" serializer.writeVariableLengthUIntBE(V::applyDefault(version));\n" " serializer.writeVariableLengthUIntBE(V::applyDefault(version));\n"
" } else {\n"
" (void)version;\n"
" }\n"; " }\n";
} }
os << " // write base classes\n";
for (const RelevantClass *baseClass : relevantBases) {
os << " serializer.write(static_cast<const ::" << baseClass->qualifiedName << " &>(customObject), version);\n";
}
os << " // write members\n"; os << " // write members\n";
auto mt = MemberTracking(); auto mt = MemberTracking();
for (clang::Decl *const decl : relevantClass.record->decls()) { for (clang::Decl *const decl : relevantClass.record->decls()) {
@ -293,7 +295,7 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
} }
mt.concludeCondition(os); mt.concludeCondition(os);
if (relevantBases.empty() && !mt.membersWritten) { if (relevantBases.empty() && !mt.membersWritten) {
os << " (void)serializer;\n (void)customObject;\n \n(void)version;"; os << " (void)serializer;\n (void)customObject;\n";
} }
os << "}\n"; os << "}\n";
@ -305,22 +307,20 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
// print readCustomType method // print readCustomType method
mt = MemberTracking(); mt = MemberTracking();
os << "template <> " << visibility << " BinaryVersion readCustomType<::" << relevantClass.qualifiedName os << "template <> " << visibility << " BinaryVersion readCustomType<::" << relevantClass.qualifiedName
<< ">(BinaryDeserializer &deserializer, ::" << relevantClass.qualifiedName << " &customObject, BinaryVersion version)\n{\n"; << ">(BinaryDeserializer &deserializer, ::" << relevantClass.qualifiedName << " &customObject)\n{\n";
os << " // read base classes\n";
for (const RelevantClass *baseClass : relevantBases) {
os << " deserializer.read(static_cast<::" << baseClass->qualifiedName << " &>(customObject), version);\n";
}
if (!relevantClass.relevantBase.empty()) { if (!relevantClass.relevantBase.empty()) {
os << " // read version\n" os << " // read version\n"
" using V = Versioning<" " auto version = BinaryVersion();\n"
" if constexpr (Versioning<"
<< relevantClass.relevantBase << relevantClass.relevantBase
<< ">;\n" << ">::enabled) {\n"
" if constexpr (V::enabled) {\n" " version = deserializer.readVariableLengthUIntBE();\n"
" V::assertVersion(version = deserializer.readVariableLengthUIntBE(), \""
<< relevantClass.qualifiedName
<< "\");\n"
" }\n"; " }\n";
} }
os << " // read base classes\n";
for (const RelevantClass *baseClass : relevantBases) {
os << " deserializer.read(static_cast<::" << baseClass->qualifiedName << " &>(customObject));\n";
}
os << " // read members\n"; os << " // read members\n";
for (clang::Decl *const decl : relevantClass.record->decls()) { for (clang::Decl *const decl : relevantClass.record->decls()) {
// check static member variables for version markers // check static member variables for version markers
@ -344,7 +344,7 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
mt.writeExtraPadding(os); mt.writeExtraPadding(os);
if (readPrivateMembers || field->getAccess() == clang::AS_public) { if (readPrivateMembers || field->getAccess() == clang::AS_public) {
os << " deserializer.read(customObject." << field->getName() << ", version);\n"; os << " deserializer.read(customObject." << field->getName() << ");\n";
mt.membersWritten = true; mt.membersWritten = true;
} }
} }

View File

@ -127,7 +127,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
} }
} }
if (relevantBases.empty() && !pushWritten) { if (relevantBases.empty() && !pushWritten) {
os << " (void)reflectable;\n (void)value;\n (void)allocator;\n"; os << " (void)reflectable;\n (void)value;\n";
} }
os << "}\n"; os << "}\n";
@ -146,7 +146,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n"; os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
} }
os << " // set error context for current record\n" os << " // set error context for current record\n"
" const char *previousRecord = nullptr;\n" " const char *previousRecord;\n"
" if (errors) {\n" " if (errors) {\n"
" previousRecord = errors->currentRecord;\n" " previousRecord = errors->currentRecord;\n"
" errors->currentRecord = \"" " errors->currentRecord = \""

View File

@ -5,8 +5,6 @@
#include <clang/AST/DeclCXX.h> #include <clang/AST/DeclCXX.h>
#include <clang/AST/DeclFriend.h> #include <clang/AST/DeclFriend.h>
#include <clang/AST/DeclTemplate.h> #include <clang/AST/DeclTemplate.h>
#include <clang/AST/PrettyPrinter.h>
#include <clang/AST/QualTypeNames.h>
#include <iostream> #include <iostream>
@ -96,13 +94,12 @@ void SerializationCodeGenerator::computeRelevantClass(RelevantClass &possiblyRel
// consider all classes inheriting from an instantiation of "JsonSerializable" relevant // consider all classes inheriting from an instantiation of "JsonSerializable" relevant
if (const auto *const relevantBase = inheritsFromInstantiationOf(possiblyRelevantClass.record, m_qualifiedNameOfRecords)) { if (const auto *const relevantBase = inheritsFromInstantiationOf(possiblyRelevantClass.record, m_qualifiedNameOfRecords)) {
auto policy = clang::PrintingPolicy(possiblyRelevantClass.record->getASTContext().getLangOpts()); cerr << "record: " << possiblyRelevantClass.qualifiedName << '\n';
policy.FullyQualifiedName = true; for (const clang::CXXBaseSpecifier base : possiblyRelevantClass.record->bases()) {
policy.SuppressScope = false; cerr << "base: " << base.getType().getAsString() << '\n';
policy.SuppressUnwrittenScope = false; }
policy.SplitTemplateClosers = false; cerr << "relevant base: " << relevantBase->getType().getAsString();
possiblyRelevantClass.relevantBase possiblyRelevantClass.relevantBase = relevantBase->getType().getAsString();
= clang::TypeName::getFullyQualifiedName(relevantBase->getType(), possiblyRelevantClass.record->getASTContext(), policy, true);
possiblyRelevantClass.isRelevant = IsRelevant::Yes; possiblyRelevantClass.isRelevant = IsRelevant::Yes;
return; return;
} }

View File

@ -13,7 +13,7 @@ template <> void pull<::TestNamespace1::Person>(::TestNamespace1::Person &refle
{ {
// pull base classes // pull base classes
// set error context for current record // set error context for current record
const char *previousRecord = nullptr; const char *previousRecord;
if (errors) { if (errors) {
previousRecord = errors->currentRecord; previousRecord = errors->currentRecord;
errors->currentRecord = "TestNamespace1::Person"; errors->currentRecord = "TestNamespace1::Person";
@ -39,7 +39,7 @@ template <> void pull<::TestNamespace2::ThirdPartyStruct>(::TestNamespace2::Thi
{ {
// pull base classes // pull base classes
// set error context for current record // set error context for current record
const char *previousRecord = nullptr; const char *previousRecord;
if (errors) { if (errors) {
previousRecord = errors->currentRecord; previousRecord = errors->currentRecord;
errors->currentRecord = "TestNamespace2::ThirdPartyStruct"; errors->currentRecord = "TestNamespace2::ThirdPartyStruct";

View File

@ -70,11 +70,6 @@ struct PointerStruct : public BinarySerializable<PointerStruct> {
std::shared_ptr<PointerTarget> s3; std::shared_ptr<PointerTarget> s3;
}; };
/*!
* \brief All of this is supposed to work if classes are within a namespace so let's use a namespace here.
*/
namespace SomeNamespace {
/*! /*!
* \brief The PointerStruct struct is used to test the behavior of the binary (de)serialization with smart pointer. * \brief The PointerStruct struct is used to test the behavior of the binary (de)serialization with smart pointer.
*/ */
@ -93,6 +88,4 @@ as_of_version(4):
}; };
// clang-format on // clang-format on
} // namespace SomeNamespace
#endif // REFLECTIVE_RAPIDJSON_TESTS_MORE_STRUCTS_H #endif // REFLECTIVE_RAPIDJSON_TESTS_MORE_STRUCTS_H

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# metadata # metadata
set(META_PROJECT_TYPE library) set(META_PROJECT_TYPE library)

View File

@ -24,17 +24,19 @@
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
namespace BinaryReflector { namespace BinaryReflector {
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType)
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version)
{ {
boost::hana::for_each(boost::hana::keys(customType), [&](auto key) { deserializer.read(boost::hana::at_key(customType, key), version); }); boost::hana::for_each(
boost::hana::keys(customType), [&deserializer, &customType](auto key) { deserializer.read(boost::hana::at_key(customType, key)); });
return 0; return 0;
} }
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> template <typename Type, Traits::EnableIf<IsCustomType<Type>> *>
void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version) void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version)
{ {
boost::hana::for_each(boost::hana::keys(customType), [&](auto key) { serializer.write(boost::hana::at_key(customType, key), version); }); CPP_UTILITIES_UNUSED(version)
boost::hana::for_each(
boost::hana::keys(customType), [&serializer, &customType](auto key) { serializer.write(boost::hana::at_key(customType, key)); });
} }
} // namespace BinaryReflector } // namespace BinaryReflector

View File

@ -16,10 +16,8 @@
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
namespace BinaryReflector { namespace BinaryReflector {
template <> template <> inline BinaryVersion readCustomType<CppUtilities::DateTime>(BinaryDeserializer &deserializer, CppUtilities::DateTime &dateTime)
inline BinaryVersion readCustomType<CppUtilities::DateTime>(BinaryDeserializer &deserializer, CppUtilities::DateTime &dateTime, BinaryVersion version)
{ {
CPP_UTILITIES_UNUSED(version)
deserializer.read(dateTime.ticks()); deserializer.read(dateTime.ticks());
return 0; return 0;
} }
@ -31,10 +29,8 @@ inline void writeCustomType<CppUtilities::DateTime>(BinarySerializer &serializer
serializer.write(dateTime.totalTicks()); serializer.write(dateTime.totalTicks());
} }
template <> template <> inline BinaryVersion readCustomType<CppUtilities::TimeSpan>(BinaryDeserializer &deserializer, CppUtilities::TimeSpan &timeSpan)
inline BinaryVersion readCustomType<CppUtilities::TimeSpan>(BinaryDeserializer &deserializer, CppUtilities::TimeSpan &timeSpan, BinaryVersion version)
{ {
CPP_UTILITIES_UNUSED(version)
deserializer.read(timeSpan.ticks()); deserializer.read(timeSpan.ticks());
return 0; return 0;
} }

View File

@ -8,7 +8,6 @@
*/ */
#include "../traits.h" #include "../traits.h"
#include "../versioning.h"
#include <c++utilities/conversion/conversionexception.h> #include <c++utilities/conversion/conversionexception.h>
#include <c++utilities/io/binaryreader.h> #include <c++utilities/io/binaryreader.h>
@ -17,7 +16,6 @@
#include <any> #include <any>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <variant> #include <variant>
@ -36,6 +34,7 @@ template <typename T> struct AdaptedBinarySerializable : public Traits::Bool<fal
}; };
using BinaryVersion = std::uint64_t; using BinaryVersion = std::uint64_t;
template <typename Type, BinaryVersion v = 0> struct BinarySerializable; template <typename Type, BinaryVersion v = 0> struct BinarySerializable;
/*! /*!
@ -47,29 +46,17 @@ namespace BinaryReflector {
template <typename Type> template <typename Type>
using IsBuiltInType = Traits::Any<Traits::IsAnyOf<Type, char, std::uint8_t, bool, std::string, std::int16_t, std::uint16_t, std::int32_t, using IsBuiltInType = Traits::Any<Traits::IsAnyOf<Type, char, std::uint8_t, bool, std::string, std::int16_t, std::uint16_t, std::int32_t,
std::uint32_t, std::int64_t, std::uint64_t, float, double>, std::uint32_t, std::int64_t, std::uint64_t, float, double>,
Traits::IsIteratable<Type>, Traits::IsSpecializingAnyOf<Type, std::pair, std::unique_ptr, std::shared_ptr, std::optional>, std::is_enum<Type>, Traits::IsIteratable<Type>, Traits::IsSpecializingAnyOf<Type, std::pair, std::unique_ptr, std::shared_ptr>, std::is_enum<Type>, IsVariant<Type>>;
IsVariant<Type>>;
template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>; template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>;
class BinaryDeserializer; class BinaryDeserializer;
class BinarySerializer; class BinarySerializer;
/// \brief Reads \a customType via \a deserializer.
/// \remarks
/// - If \tp Type is versioned, the version is determined from the data. Otherwise \a version is assumed.
/// - The determined or specified \a version shall be passed to nested invocations.
/// \returns Returns the determined/assumed version.
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr>
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version = 0); BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType);
/// \brief Writes \a customType via \a serializer.
/// \remarks
/// - If \tp Type is versioned, \a version is prepended to the data.
/// - The specified \a version shall be passed to nested invocations.
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr>
void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version = 0); void writeCustomType(BinarySerializer &serializer, const Type &customType, BinaryVersion version = 0);
/// \brief The BinaryDeserializer class can read various data types, including custom ones, from an std::istream.
class BinaryDeserializer : public CppUtilities::BinaryReader { class BinaryDeserializer : public CppUtilities::BinaryReader {
friend class ::BinaryReflectorTests; friend class ::BinaryReflectorTests;
@ -80,7 +67,6 @@ public:
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void read(Type &pair); template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void read(Type &pair);
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> * = nullptr> void read(Type &pointer); template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::unique_ptr>> * = nullptr> void read(Type &pointer);
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr> void read(Type &pointer); template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr> void read(Type &pointer);
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> * = nullptr> void read(Type &pointer);
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> * = nullptr> void read(Type &iteratable); template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> * = nullptr> void read(Type &iteratable);
template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr> void read(Type &iteratable); template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr> void read(Type &iteratable);
template <typename Type, template <typename Type,
@ -89,14 +75,12 @@ public:
void read(Type &iteratable); void read(Type &iteratable);
template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void read(Type &enumValue); template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void read(Type &enumValue);
template <typename Type, Traits::EnableIf<IsVariant<Type>> * = nullptr> void read(Type &variant); template <typename Type, Traits::EnableIf<IsVariant<Type>> * = nullptr> void read(Type &variant);
template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> * = nullptr> BinaryVersion read(Type &builtInType, BinaryVersion version); template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> BinaryVersion read(Type &customType);
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> BinaryVersion read(Type &customType, BinaryVersion version = 0);
private: private:
std::unordered_map<std::uint64_t, std::any> m_pointer; std::unordered_map<std::uint64_t, std::any> m_pointer;
}; };
/// \brief The BinarySerializer class can write various data types, including custom ones, to an std::ostream.
class BinarySerializer : public CppUtilities::BinaryWriter { class BinarySerializer : public CppUtilities::BinaryWriter {
friend class ::BinaryReflectorTests; friend class ::BinaryReflectorTests;
@ -105,8 +89,7 @@ public:
using CppUtilities::BinaryWriter::write; using CppUtilities::BinaryWriter::write;
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void write(const Type &pair); template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::pair>> * = nullptr> void write(const Type &pair);
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> * = nullptr> template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr>> * = nullptr> void write(const Type &pointer);
void write(const Type &pointer);
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> * = nullptr> void write(const Type &pointer); template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::shared_ptr>> * = nullptr> void write(const Type &pointer);
template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> * = nullptr> void write(const Type &iteratable); template <typename Type, Traits::EnableIf<IsIteratableExceptString<Type>, Traits::HasSize<Type>> * = nullptr> void write(const Type &iteratable);
template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void write(const Type &enumValue); template <typename Type, Traits::EnableIf<std::is_enum<Type>> * = nullptr> void write(const Type &enumValue);
@ -163,16 +146,6 @@ template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::
} }
} }
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> *> void BinaryDeserializer::read(Type &opt)
{
if (!readBool()) {
opt.reset();
return;
}
opt = std::make_optional<typename Type::value_type>();
read(*opt);
}
template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> *> void BinaryDeserializer::read(Type &iteratable) template <typename Type, Traits::EnableIf<IsArray<Type>, Traits::IsResizable<Type>> *> void BinaryDeserializer::read(Type &iteratable)
{ {
const auto size = readVariableLengthUIntBE(); const auto size = readVariableLengthUIntBE();
@ -239,15 +212,9 @@ template <typename Type, Traits::EnableIf<IsVariant<Type>> *> void BinaryDeseria
Detail::readVariantValueByRuntimeIndex(readByte(), variant, *this); Detail::readVariantValueByRuntimeIndex(readByte(), variant, *this);
} }
template <typename Type, Traits::EnableIf<IsBuiltInType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &builtInType, BinaryVersion version) template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &customType)
{ {
read(builtInType); return readCustomType(*this, customType);
return version;
}
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &customType, BinaryVersion version)
{
return readCustomType(*this, customType, version);
} }
inline BinarySerializer::BinarySerializer(std::ostream *stream) inline BinarySerializer::BinarySerializer(std::ostream *stream)
@ -261,12 +228,12 @@ template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::
write(pair.second); write(pair.second);
} }
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> *> template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr>> *> void BinarySerializer::write(const Type &pointer)
void BinarySerializer::write(const Type &opt)
{ {
writeBool(static_cast<bool>(opt)); const bool hasValue = pointer != nullptr;
if (opt) { writeBool(hasValue);
write(*opt); if (hasValue) {
write(*pointer);
} }
} }

View File

@ -14,23 +14,20 @@
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
using BinaryVersionNotSupported = VersionNotSupported<BinaryVersion>;
/*! /*!
* \brief The BinarySerializable class provides the CRTP-base for (de)serializable objects. * \brief The BinarySerializable class provides the CRTP-base for (de)serializable objects.
*/ */
template <typename Type, BinaryVersion v> struct BinarySerializable { template <typename Type, BinaryVersion v> struct BinarySerializable {
using VersionNotSupported = BinaryVersionNotSupported;
void toBinary(std::ostream &outputStream, BinaryVersion version = 0) const; void toBinary(std::ostream &outputStream, BinaryVersion version = 0) const;
BinaryVersion restoreFromBinary(std::istream &inputStream); BinaryVersion restoreFromBinary(std::istream &inputStream);
static Type fromBinary(std::istream &inputStream); static Type fromBinary(std::istream &inputStream);
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::BinarySerializable"; static constexpr const char *qualifiedName = "ReflectiveRapidJSON::BinarySerializable";
static constexpr auto version = v; static constexpr auto version = v;
static constexpr auto versioningEnabled(const BinarySerializable<Type, v> &)
#if __cplusplus > 201707L {
bool operator==(const BinarySerializable<Type, v> &) const = default; return v != 0;
#endif }
}; };
template <typename Type, BinaryVersion v> inline void BinarySerializable<Type, v>::toBinary(std::ostream &outputStream, BinaryVersion version) const template <typename Type, BinaryVersion v> inline void BinarySerializable<Type, v>::toBinary(std::ostream &outputStream, BinaryVersion version) const
@ -58,7 +55,8 @@ template <typename Type, BinaryVersion v> Type BinarySerializable<Type, v>::from
* Find out whether this is a compiler bug or a correct error message. * Find out whether this is a compiler bug or a correct error message.
*/ */
#define REFLECTIVE_RAPIDJSON_MAKE_BINARY_SERIALIZABLE(T) \ #define REFLECTIVE_RAPIDJSON_MAKE_BINARY_SERIALIZABLE(T) \
template <> struct ReflectiveRapidJSON::AdaptedBinarySerializable<T> : Traits::Bool<true> {} template <> struct ReflectiveRapidJSON::AdaptedBinarySerializable<T> : Traits::Bool<true> { \
}
} // namespace ReflectiveRapidJSON } // namespace ReflectiveRapidJSON

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR) cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
# prevent multiple inclusion # prevent multiple inclusion
if (DEFINED REFLECTION_GENERATOR_MODULE_LOADED) if (DEFINED REFLECTION_GENERATOR_MODULE_LOADED)
@ -44,10 +44,10 @@ if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR)
message(FATAL_ERROR "Unable to find the clang executable to determine Clang's resource directory") message(FATAL_ERROR "Unable to find the clang executable to determine Clang's resource directory")
endif () endif ()
endif () endif ()
execute_process( exec_program(
COMMAND ${REFLECTION_GENERATOR_CLANG_BIN} -print-resource-dir ${REFLECTION_GENERATOR_CLANG_BIN} ARGS
OUTPUT_VARIABLE REFLECTION_GENERATOR_CLANG_RESOURCE_DIR -print-resource-dir
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE REFLECTION_GENERATOR_CLANG_RESOURCE_DIR)
endif () endif ()
if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR OR NOT IS_DIRECTORY "${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR}") if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR OR NOT IS_DIRECTORY "${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR}")
message( message(
@ -87,7 +87,7 @@ endfunction ()
include(CMakeParseArguments) include(CMakeParseArguments)
function (add_reflection_generator_invocation) function (add_reflection_generator_invocation)
# parse arguments # parse arguments
set(OPTIONAL_ARGS ERROR_RESILIENT) set(OPTIONAL_ARGS)
set(ONE_VALUE_ARGS OUTPUT_DIRECTORY JSON_VISIBILITY BINARY_VISBILITY) set(ONE_VALUE_ARGS OUTPUT_DIRECTORY JSON_VISIBILITY BINARY_VISBILITY)
set(MULTI_VALUE_ARGS set(MULTI_VALUE_ARGS
INPUT_FILES INPUT_FILES
@ -121,25 +121,30 @@ function (add_reflection_generator_invocation)
list(APPEND ARGS_CLANG_OPTIONS -I "${INCLUDE_DIR}") list(APPEND ARGS_CLANG_OPTIONS -I "${INCLUDE_DIR}")
endforeach () endforeach ()
# avoid including headers from host when cross compiling # add workaround for cross compiling with mingw-w64 to prevent host stdlib.h being included (not sure why specifying
if (CMAKE_CROSSCOMPILING) # REFLECTION_GENERATOR_INCLUDE_DIRECTORIES is not enough to let it find this particular header file)
list(APPEND ARGS_CLANG_OPTIONS -nostdinc) if (MINGW)
# find MinGW version of stdlib.h
find_file(MINGW_W64_STDLIB_H stdlib.h ${REFLECTION_GENERATOR_INCLUDE_DIRECTORIES})
if (NOT EXISTS "${MINGW_W64_STDLIB_H}")
message(
FATAL_ERROR
"Unable to locate MinGW version of stdlib.h. Ensure it is in REFLECTION_GENERATOR_INCLUDE_DIRECTORIES.")
endif ()
# ensure libtooling includes the MinGW version of stdlib.h rather than the host version
list(APPEND ARGS_CLANG_OPTIONS -include "${MINGW_W64_STDLIB_H}" -D_STDLIB_H # prevent processing of host stdlib.h
)
endif () endif ()
# add options to be passed to clang from the specified targets # add options to be passed to clang from the specified targets
if (ARGS_CLANG_OPTIONS_FROM_TARGETS) if (ARGS_CLANG_OPTIONS_FROM_TARGETS)
foreach (TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_TARGETS}) foreach (TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_TARGETS})
# set c++ standard # set c++ standard
list( list(APPEND ARGS_CLANG_OPTIONS "-std=c++$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>")
APPEND # add compile flags
ARGS_CLANG_OPTIONS
"$<$<BOOL:$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>:-std=c++$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>"
)
# add compile flags and options
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_FLAGS) _reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_FLAGS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>") list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>")
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_OPTIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>")
# add compile definitions # add compile definitions
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_DEFINITIONS) _reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_DEFINITIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-D$<JOIN:${PROP},$<SEMICOLON>-D>>") list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-D$<JOIN:${PROP},$<SEMICOLON>-D>>")
@ -190,9 +195,6 @@ function (add_reflection_generator_invocation)
if (ARGS_BINARY_VISBILITY) if (ARGS_BINARY_VISBILITY)
list(APPEND CLI_ARGUMENTS --binary-visibility "${ARGS_BINARY_VISBILITY}") list(APPEND CLI_ARGUMENTS --binary-visibility "${ARGS_BINARY_VISBILITY}")
endif () endif ()
if (ARGS_ERROR_RESILIENT)
list(APPEND CLI_ARGUMENTS --error-resilient)
endif ()
add_custom_command( add_custom_command(
OUTPUT "${OUTPUT_FILE}" OUTPUT "${OUTPUT_FILE}"
COMMAND "${REFLECTION_GENERATOR_EXECUTABLE}" ARGS ${CLI_ARGUMENTS} COMMAND "${REFLECTION_GENERATOR_EXECUTABLE}" ARGS ${CLI_ARGUMENTS}

View File

@ -1 +0,0 @@
../../versioning.h

View File

@ -155,7 +155,6 @@ struct JsonDeserializationErrors : public std::vector<JsonDeserializationError>
JsonDeserializationErrors(); JsonDeserializationErrors();
template <typename ExpectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType); template <typename ExpectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType);
template <RAPIDJSON_NAMESPACE::Type expectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType);
void reportArraySizeMismatch(); void reportArraySizeMismatch();
void reportConversionError(JsonType jsonType); void reportConversionError(JsonType jsonType);
void reportUnexpectedDuplicate(JsonType jsonType); void reportUnexpectedDuplicate(JsonType jsonType);
@ -221,16 +220,6 @@ template <typename ExpectedType> inline void JsonDeserializationErrors::reportTy
throwMaybe(ThrowOn::TypeMismatch); throwMaybe(ThrowOn::TypeMismatch);
} }
/*!
* \brief Reports a type mismatch between \tparam expectedType and \a presentType within the current context.
*/
template <RAPIDJSON_NAMESPACE::Type expectedType> inline void JsonDeserializationErrors::reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType)
{
emplace_back(
JsonDeserializationErrorKind::TypeMismatch, jsonType(expectedType), jsonType(presentType), currentRecord, currentMember, currentIndex);
throwMaybe(ThrowOn::TypeMismatch);
}
/*! /*!
* \brief Reports an array size mismatch. * \brief Reports an array size mismatch.
* \todo Allow specifying expected and actual size. * \todo Allow specifying expected and actual size.

View File

@ -19,13 +19,11 @@
#include <limits> #include <limits>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional>
#include <set> #include <set>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility>
#include <variant> #include <variant>
#include "./errorhandling.h" #include "./errorhandling.h"
@ -83,8 +81,9 @@ inline RAPIDJSON_NAMESPACE::Document parseJsonDocFromString(const char *json, st
// define traits to distinguish between "built-in" types like int, std::string, std::vector, ... and custom structs/classes // define traits to distinguish between "built-in" types like int, std::string, std::vector, ... and custom structs/classes
template <typename Type> template <typename Type>
using IsBuiltInType = Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>, std::is_enum<Type>, using IsBuiltInType = Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>, std::is_pointer<Type>, std::is_enum<Type>,
Traits::IsSpecializingAnyOf<Type, std::tuple, std::pair>, Traits::IsIteratable<Type>, Traits::IsSpecializationOf<Type, std::tuple>, Traits::IsSpecializationOf<Type, std::pair>, Traits::IsIteratable<Type>,
Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::shared_ptr, std::weak_ptr, std::optional>, IsVariant<Type>>; Traits::IsSpecializationOf<Type, std::unique_ptr>, Traits::IsSpecializationOf<Type, std::shared_ptr>,
Traits::IsSpecializationOf<Type, std::weak_ptr>, IsVariant<Type>>;
template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>; template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>;
// define trait to check for custom structs/classes which are JSON serializable // define trait to check for custom structs/classes which are JSON serializable
@ -148,24 +147,12 @@ inline void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAP
/*! /*!
* \brief Pushes the specified integer/float/boolean to the specified value. * \brief Pushes the specified integer/float/boolean to the specified value.
*/ */
template <typename Type, template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>> * = nullptr>
Traits::EnableIfAny<
Traits::All<std::is_integral<Type>, Traits::Not<std::is_same<Type, std::uint8_t>>, Traits::Not<std::is_same<Type, std::int8_t>>>,
std::is_floating_point<Type>> * = nullptr>
inline void push(Type reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) inline void push(Type reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{ {
value.Set(reflectable, allocator); value.Set(reflectable, allocator);
} }
/*!
* \brief Pushes the specified 8-bit integer to the specified value.
*/
template <typename Type, Traits::EnableIfAny<std::is_same<Type, std::uint8_t>, std::is_same<Type, std::int8_t>> * = nullptr>
inline void push(Type reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{
value.Set(static_cast<int>(reflectable), allocator);
}
/*! /*!
* \brief Pushes the specified enumeration item to the specified value. * \brief Pushes the specified enumeration item to the specified value.
*/ */
@ -319,10 +306,11 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_
} }
/*! /*!
* \brief Pushes the specified unique_ptr, shared_ptr, weak_ptr or optional to the specified value. * \brief Pushes the specified unique_ptr, shared_ptr or weak_ptr to the specified value.
*/ */
template <typename Type, template <typename Type,
Traits::EnableIfAny<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::shared_ptr, std::weak_ptr, std::optional>> * = nullptr> Traits::EnableIfAny<Traits::IsSpecializationOf<Type, std::unique_ptr>, Traits::IsSpecializationOf<Type, std::shared_ptr>,
Traits::IsSpecializationOf<Type, std::weak_ptr>> * = nullptr>
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{ {
if (!reflectable) { if (!reflectable) {
@ -491,12 +479,6 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr> template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::shared_ptr>> * = nullptr>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors); void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
/*!
* \brief Pulls the specified \a reflectable which is an std::optional from the specified value which might be null.
*/
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> * = nullptr>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
/*! /*!
* \brief Pulls the specified \a reflectable which is a variant from the specified value which might be null. * \brief Pulls the specified \a reflectable which is a variant from the specified value which might be null.
*/ */
@ -529,8 +511,7 @@ void pull(Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_N
* \brief Pulls the integer or float from the specified value which is supposed and checked to contain the right type. * \brief Pulls the integer or float from the specified value which is supposed and checked to contain the right type.
*/ */
template <typename Type, template <typename Type,
Traits::EnableIf<Traits::Not<std::is_same<Type, bool>>, Traits::Not<std::is_same<Type, std::uint8_t>>, Traits::EnableIf<Traits::Not<std::is_same<Type, bool>>, Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>>> * = nullptr>
Traits::Not<std::is_same<Type, std::int8_t>>, Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>>> * = nullptr>
inline void pull( inline void pull(
Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors) Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{ {
@ -543,20 +524,6 @@ inline void pull(
reflectable = value.Is<Type>() ? value.Get<Type>() : static_cast<Type>(value.GetDouble()); reflectable = value.Is<Type>() ? value.Get<Type>() : static_cast<Type>(value.GetDouble());
} }
/*!
* \brief Pulls the integer or float from the specified value which is supposed and checked to contain the right type.
*/
template <typename Type, Traits::EnableIfAny<std::is_same<Type, std::uint8_t>, std::is_same<Type, std::int8_t>> * = nullptr>
inline void pull(
Type &reflectable, const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
int i = 0;
pull(i, value, errors);
if (value.IsNumber()) {
reflectable = static_cast<Type>(i);
}
}
/*! /*!
* \brief Pulls the boolean from the specified value which is supposed and checked to contain the right type. * \brief Pulls the boolean from the specified value which is supposed and checked to contain the right type.
*/ */
@ -701,7 +668,7 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
++index; ++index;
typename Type::value_type itemObj; typename Type::value_type itemObj;
pull(itemObj, item, errors); pull(itemObj, item, errors);
reflectable.emplace(std::move(itemObj)); reflectable.emplace(move(itemObj));
} }
// clear error context // clear error context
@ -729,7 +696,7 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
++index; ++index;
typename Type::value_type itemObj; typename Type::value_type itemObj;
pull(itemObj, item, errors); pull(itemObj, item, errors);
if (!reflectable.emplace(std::move(itemObj)).second) { if (!reflectable.emplace(move(itemObj)).second) {
errors->reportUnexpectedDuplicate(JsonType::Array); errors->reportUnexpectedDuplicate(JsonType::Array);
} }
} }
@ -882,20 +849,6 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
pull(*reflectable, value, errors); pull(*reflectable, value, errors);
} }
/*!
* \brief Pulls the specified \a reflectable which is an std::optional from the specified value which might be null.
*/
template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::optional>> *>
void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (value.IsNull()) {
reflectable.reset();
return;
}
reflectable = std::make_optional<typename Type::value_type>();
pull(*reflectable, value, errors);
}
/// \cond /// \cond
namespace Detail { namespace Detail {
template <typename Variant, std::size_t compiletimeIndex = 0> template <typename Variant, std::size_t compiletimeIndex = 0>
@ -982,7 +935,7 @@ inline void pull(Type &reflectable, const char *name, const rapidjson::GenericVa
} }
// set error context for current member // set error context for current member
const char *previousMember = nullptr; const char *previousMember;
if (errors) { if (errors) {
previousMember = errors->currentMember; previousMember = errors->currentMember;
errors->currentMember = name; errors->currentMember = name;

View File

@ -31,10 +31,6 @@ template <typename Type> struct JsonSerializable {
static Type fromJson(const std::string &json, JsonDeserializationErrors *errors = nullptr); static Type fromJson(const std::string &json, JsonDeserializationErrors *errors = nullptr);
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::JsonSerializable"; static constexpr const char *qualifiedName = "ReflectiveRapidJSON::JsonSerializable";
#if __cplusplus > 201707L
bool operator==(const JsonSerializable<Type> &) const = default;
#endif
}; };
/*! /*!
@ -120,7 +116,8 @@ const JsonSerializable<Type> &as(const Type &serializable)
* Find out whether this is a compiler bug or a correct error message. * Find out whether this is a compiler bug or a correct error message.
*/ */
#define REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(T) \ #define REFLECTIVE_RAPIDJSON_MAKE_JSON_SERIALIZABLE(T) \
template <> struct ReflectiveRapidJSON::AdaptedJsonSerializable<T> : Traits::Bool<true> {} template <> struct ReflectiveRapidJSON::AdaptedJsonSerializable<T> : Traits::Bool<true> { \
}
/*! /*!
* \def The REFLECTIVE_RAPIDJSON_PUSH_PRIVATE_MEMBERS macro enables serialization of private members. * \def The REFLECTIVE_RAPIDJSON_PUSH_PRIVATE_MEMBERS macro enables serialization of private members.

View File

@ -75,9 +75,8 @@ struct ObjectWithVariantsBinary : public BinarySerializable<ObjectWithVariantsBi
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
namespace BinaryReflector { namespace BinaryReflector {
template <> BinaryVersion readCustomType<TestObjectBinary>(BinaryDeserializer &deserializer, TestObjectBinary &customType, BinaryVersion version) template <> BinaryVersion readCustomType<TestObjectBinary>(BinaryDeserializer &deserializer, TestObjectBinary &customType)
{ {
CPP_UTILITIES_UNUSED(version)
deserializer.read(customType.number); deserializer.read(customType.number);
deserializer.read(customType.number2); deserializer.read(customType.number2);
deserializer.read(customType.numbers); deserializer.read(customType.numbers);
@ -116,9 +115,8 @@ template <> void writeCustomType<TestObjectBinary>(BinarySerializer &serializer,
serializer.write(customType.dateTime); serializer.write(customType.dateTime);
} }
template <> BinaryVersion readCustomType<NestingArrayBinary>(BinaryDeserializer &deserializer, NestingArrayBinary &customType, BinaryVersion version) template <> BinaryVersion readCustomType<NestingArrayBinary>(BinaryDeserializer &deserializer, NestingArrayBinary &customType)
{ {
CPP_UTILITIES_UNUSED(version)
deserializer.read(customType.name); deserializer.read(customType.name);
deserializer.read(customType.testObjects); deserializer.read(customType.testObjects);
return 0; return 0;
@ -126,15 +124,12 @@ template <> BinaryVersion readCustomType<NestingArrayBinary>(BinaryDeserializer
template <> void writeCustomType<NestingArrayBinary>(BinarySerializer &serializer, const NestingArrayBinary &customType, BinaryVersion version) template <> void writeCustomType<NestingArrayBinary>(BinarySerializer &serializer, const NestingArrayBinary &customType, BinaryVersion version)
{ {
CPP_UTILITIES_UNUSED(version)
serializer.write(customType.name); serializer.write(customType.name);
serializer.write(customType.testObjects); serializer.write(customType.testObjects);
} }
template <> template <> BinaryVersion readCustomType<ObjectWithVariantsBinary>(BinaryDeserializer &deserializer, ObjectWithVariantsBinary &customType)
BinaryVersion readCustomType<ObjectWithVariantsBinary>(BinaryDeserializer &deserializer, ObjectWithVariantsBinary &customType, BinaryVersion version)
{ {
CPP_UTILITIES_UNUSED(version)
deserializer.read(customType.someVariant); deserializer.read(customType.someVariant);
deserializer.read(customType.anotherVariant); deserializer.read(customType.anotherVariant);
deserializer.read(customType.yetAnotherVariant); deserializer.read(customType.yetAnotherVariant);
@ -170,7 +165,6 @@ class BinaryReflectorTests : public TestFixture {
CPPUNIT_TEST(testSmallSharedPointer); CPPUNIT_TEST(testSmallSharedPointer);
CPPUNIT_TEST(testBigSharedPointer); CPPUNIT_TEST(testBigSharedPointer);
CPPUNIT_TEST(testVariant); CPPUNIT_TEST(testVariant);
CPPUNIT_TEST(testOptional);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
@ -188,7 +182,6 @@ public:
void testSmallSharedPointer(); void testSmallSharedPointer();
void testBigSharedPointer(); void testBigSharedPointer();
void testVariant(); void testVariant();
void testOptional();
private: private:
vector<unsigned char> m_buffer; vector<unsigned char> m_buffer;
@ -273,44 +266,13 @@ void BinaryReflectorTests::tearDown()
{ {
} }
static void setBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
{
#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
#else
CPP_UTILITIES_UNUSED(stream)
CPP_UTILITIES_UNUSED(buffer)
CPP_UTILITIES_UNUSED(bufferSize)
#endif
}
static void readBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
{
#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
CPP_UTILITIES_UNUSED(stream)
CPP_UTILITIES_UNUSED(buffer)
CPP_UTILITIES_UNUSED(bufferSize)
#else
stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
#endif
}
static void writeBuffer(std::stringstream &stream, unsigned char *buffer, std::size_t bufferSize)
{
#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bufferSize));
#else
stream.write(reinterpret_cast<const char *>(buffer), static_cast<std::streamsize>(bufferSize));
#endif
}
void BinaryReflectorTests::testSerializeSimpleStruct() void BinaryReflectorTests::testSerializeSimpleStruct()
{ {
stringstream stream(ios_base::out | ios_base::binary); stringstream stream(ios_base::out | ios_base::binary);
stream.exceptions(ios_base::failbit | ios_base::badbit); stream.exceptions(ios_base::failbit | ios_base::badbit);
m_buffer.resize(m_expectedTestObj.size()); m_buffer.resize(m_expectedTestObj.size());
setBuffer(stream, m_buffer.data(), m_buffer.size()); stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_buffer.data()), static_cast<streamsize>(m_buffer.size()));
m_testObj.toBinary(stream); m_testObj.toBinary(stream);
readBuffer(stream, m_buffer.data(), m_buffer.size());
CPPUNIT_ASSERT_EQUAL(m_expectedTestObj, m_buffer); CPPUNIT_ASSERT_EQUAL(m_expectedTestObj, m_buffer);
} }
@ -319,7 +281,7 @@ void BinaryReflectorTests::testDeserializeSimpleStruct()
{ {
stringstream stream(ios_base::in | ios_base::binary); stringstream stream(ios_base::in | ios_base::binary);
stream.exceptions(ios_base::failbit | ios_base::badbit); stream.exceptions(ios_base::failbit | ios_base::badbit);
writeBuffer(stream, m_expectedTestObj.data(), m_expectedTestObj.size()); stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_expectedTestObj.data()), static_cast<streamsize>(m_expectedTestObj.size()));
const auto deserialized(TestObjectBinary::fromBinary(stream)); const auto deserialized(TestObjectBinary::fromBinary(stream));
assertTestObject(deserialized); assertTestObject(deserialized);
} }
@ -329,9 +291,8 @@ void BinaryReflectorTests::testSerializeNestedStruct()
stringstream stream(ios_base::out | ios_base::binary); stringstream stream(ios_base::out | ios_base::binary);
stream.exceptions(ios_base::failbit | ios_base::badbit); stream.exceptions(ios_base::failbit | ios_base::badbit);
m_buffer.resize(m_expectedNestedTestObj.size()); m_buffer.resize(m_expectedNestedTestObj.size());
setBuffer(stream, m_buffer.data(), m_buffer.size()); stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_buffer.data()), static_cast<streamsize>(m_buffer.size()));
m_nestedTestObj.toBinary(stream); m_nestedTestObj.toBinary(stream);
readBuffer(stream, m_buffer.data(), m_buffer.size());
CPPUNIT_ASSERT_EQUAL(m_expectedNestedTestObj, m_buffer); CPPUNIT_ASSERT_EQUAL(m_expectedNestedTestObj, m_buffer);
} }
@ -340,7 +301,7 @@ void BinaryReflectorTests::testDeserializeNestedStruct()
{ {
stringstream stream(ios_base::in | ios_base::binary); stringstream stream(ios_base::in | ios_base::binary);
stream.exceptions(ios_base::failbit | ios_base::badbit); stream.exceptions(ios_base::failbit | ios_base::badbit);
writeBuffer(stream, m_expectedNestedTestObj.data(), m_expectedNestedTestObj.size()); stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_expectedNestedTestObj.data()), static_cast<streamsize>(m_expectedNestedTestObj.size()));
const auto deserialized(NestingArrayBinary::fromBinary(stream)); const auto deserialized(NestingArrayBinary::fromBinary(stream));
CPPUNIT_ASSERT_EQUAL(m_nestedTestObj.name, deserialized.name); CPPUNIT_ASSERT_EQUAL(m_nestedTestObj.name, deserialized.name);
@ -425,28 +386,3 @@ void BinaryReflectorTests::testVariant()
CPPUNIT_ASSERT_EQUAL("foo"s, get<0>(deserializedVariants.anotherVariant)); CPPUNIT_ASSERT_EQUAL("foo"s, get<0>(deserializedVariants.anotherVariant));
CPPUNIT_ASSERT_EQUAL(42, get<1>(deserializedVariants.yetAnotherVariant)); CPPUNIT_ASSERT_EQUAL(42, get<1>(deserializedVariants.yetAnotherVariant));
} }
void BinaryReflectorTests::testOptional()
{
// create test objects
const auto str = std::make_optional<std::string>("foo");
const auto nullStr = std::optional<std::string>();
// serialize test object
auto stream = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
stream.exceptions(std::ios_base::failbit | std::ios_base::badbit);
auto ser = BinaryReflector::BinarySerializer(&stream);
ser.write(str);
ser.write(nullStr);
// deserialize the object again
auto deser = BinaryReflector::BinaryDeserializer(&stream);
auto deserStr = std::optional<std::string>();
auto deserNullStr = std::optional<std::string>();
deser.read(deserStr);
deser.read(deserNullStr);
CPPUNIT_ASSERT(deserStr.has_value());
CPPUNIT_ASSERT_EQUAL("foo"s, deserStr.value());
CPPUNIT_ASSERT(!nullStr.has_value());
}

View File

@ -112,7 +112,7 @@ template <> inline void push<NestingArray>(const NestingArray &reflectable, Valu
template <> template <>
inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors) inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
{ {
const char *previousRecord = nullptr; const char *previousRecord;
if (errors) { if (errors) {
previousRecord = errors->currentRecord; previousRecord = errors->currentRecord;
errors->currentRecord = "TestObject"; errors->currentRecord = "TestObject";
@ -141,7 +141,7 @@ inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<ch
template <> template <>
inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors) inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
{ {
const char *previousRecord = nullptr; const char *previousRecord;
if (errors) { if (errors) {
previousRecord = errors->currentRecord; previousRecord = errors->currentRecord;
errors->currentRecord = "NestingObject"; errors->currentRecord = "NestingObject";
@ -156,7 +156,7 @@ inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<U
template <> template <>
inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors) inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
{ {
const char *previousRecord = nullptr; const char *previousRecord;
if (errors) { if (errors) {
previousRecord = errors->currentRecord; previousRecord = errors->currentRecord;
errors->currentRecord = "NestingArray"; errors->currentRecord = "NestingArray";
@ -186,13 +186,11 @@ class JsonReflectorTests : public TestFixture {
CPPUNIT_TEST(testSerializeNestedObjects); CPPUNIT_TEST(testSerializeNestedObjects);
CPPUNIT_TEST(testSerializeUniquePtr); CPPUNIT_TEST(testSerializeUniquePtr);
CPPUNIT_TEST(testSerializeSharedPtr); CPPUNIT_TEST(testSerializeSharedPtr);
CPPUNIT_TEST(testSerializeOptional);
CPPUNIT_TEST(testDeserializePrimitives); CPPUNIT_TEST(testDeserializePrimitives);
CPPUNIT_TEST(testDeserializeSimpleObjects); CPPUNIT_TEST(testDeserializeSimpleObjects);
CPPUNIT_TEST(testDeserializeNestedObjects); CPPUNIT_TEST(testDeserializeNestedObjects);
CPPUNIT_TEST(testDeserializeUniquePtr); CPPUNIT_TEST(testDeserializeUniquePtr);
CPPUNIT_TEST(testDeserializeSharedPtr); CPPUNIT_TEST(testDeserializeSharedPtr);
CPPUNIT_TEST(testDeserializeOptional);
CPPUNIT_TEST(testHandlingParseError); CPPUNIT_TEST(testHandlingParseError);
CPPUNIT_TEST(testHandlingTypeMismatch); CPPUNIT_TEST(testHandlingTypeMismatch);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
@ -207,13 +205,11 @@ public:
void testSerializeNestedObjects(); void testSerializeNestedObjects();
void testSerializeUniquePtr(); void testSerializeUniquePtr();
void testSerializeSharedPtr(); void testSerializeSharedPtr();
void testSerializeOptional();
void testDeserializePrimitives(); void testDeserializePrimitives();
void testDeserializeSimpleObjects(); void testDeserializeSimpleObjects();
void testDeserializeNestedObjects(); void testDeserializeNestedObjects();
void testDeserializeUniquePtr(); void testDeserializeUniquePtr();
void testDeserializeSharedPtr(); void testDeserializeSharedPtr();
void testDeserializeOptional();
void testHandlingParseError(); void testHandlingParseError();
void testHandlingTypeMismatch(); void testHandlingTypeMismatch();
@ -383,28 +379,6 @@ void JsonReflectorTests::testSerializeSharedPtr()
string(strbuf.GetString())); string(strbuf.GetString()));
} }
/*!
* \brief Tests serializing std::optional.
*/
void JsonReflectorTests::testSerializeOptional()
{
Document doc(kArrayType);
Document::AllocatorType &alloc = doc.GetAllocator();
doc.SetArray();
Document::Array array(doc.GetArray());
const auto str = make_optional<std::string>("foo");
const auto nullStr = std::optional<std::string>();
JsonReflector::push(str, array, alloc);
JsonReflector::push(nullStr, array, alloc);
StringBuffer strbuf;
Writer<StringBuffer> jsonWriter(strbuf);
doc.Accept(jsonWriter);
CPPUNIT_ASSERT_EQUAL("[\"foo\",null]"s, std::string(strbuf.GetString()));
}
/*! /*!
* \brief Tests deserializing strings, numbers (int, float, double) and boolean. * \brief Tests deserializing strings, numbers (int, float, double) and boolean.
*/ */
@ -543,9 +517,6 @@ void JsonReflectorTests::testDeserializeNestedObjects()
CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text); CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
} }
/*!
* \brief Tests deserializing std::optional.
*/
void JsonReflectorTests::testDeserializeUniquePtr() void JsonReflectorTests::testDeserializeUniquePtr()
{ {
Document doc(kArrayType); Document doc(kArrayType);
@ -590,22 +561,6 @@ void JsonReflectorTests::testDeserializeSharedPtr()
CPPUNIT_ASSERT_EQUAL("bar"s, obj->text); CPPUNIT_ASSERT_EQUAL("bar"s, obj->text);
} }
void JsonReflectorTests::testDeserializeOptional()
{
Document doc(kArrayType);
doc.Parse("[\"foo\",null]");
auto array = doc.GetArray().begin();
optional<string> str = "foo"s;
optional<string> nullStr;
JsonDeserializationErrors errors;
JsonReflector::pull(str, array, &errors);
CPPUNIT_ASSERT_EQUAL(0_st, errors.size());
CPPUNIT_ASSERT(str.has_value());
CPPUNIT_ASSERT_EQUAL("foo"s, *str);
CPPUNIT_ASSERT(!nullStr.has_value());
}
/*! /*!
* \brief Tests whether RAPIDJSON_NAMESPACE::ParseResult is thrown correctly when passing invalid JSON to fromJSON(). * \brief Tests whether RAPIDJSON_NAMESPACE::ParseResult is thrown correctly when passing invalid JSON to fromJSON().
*/ */

View File

@ -7,14 +7,20 @@
#include <vector> #include <vector>
// define structs for testing REFLECTIVE_RAPIDJSON_TREAT_AS_… // define structs for testing REFLECTIVE_RAPIDJSON_TREAT_AS_…
struct Foo {}; struct Foo {
struct Bar {}; };
struct Bar {
};
// define structs for testing versioning // define structs for testing versioning
struct VersionlessBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase> {}; struct VersionlessBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase> {
struct VersionedDerived : public VersionlessBase, public ReflectiveRapidJSON::BinarySerializable<VersionedDerived, 1> {}; };
struct VersionedBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase, 1> {}; struct VersionedDerived : public VersionlessBase, public ReflectiveRapidJSON::BinarySerializable<VersionedDerived, 1> {
struct VersionlessDerived : public VersionedBase, public ReflectiveRapidJSON::BinarySerializable<VersionlessDerived> {}; };
struct VersionedBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase, 1> {
};
struct VersionlessDerived : public VersionedBase, public ReflectiveRapidJSON::BinarySerializable<VersionlessDerived> {
};
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(Foo); REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(Foo);

View File

@ -16,22 +16,30 @@ namespace Traits = ::CppUtilities::Traits;
// define structs and macros to allow treating custom data types as std::map, std::set, ... // define structs and macros to allow treating custom data types as std::map, std::set, ...
/// \brief \brief The TreatAsMapOrHash class allows treating custom classes as std::map or std::unordered_map. /// \brief \brief The TreatAsMapOrHash class allows treating custom classes as std::map or std::unordered_map.
template <typename T> struct TreatAsMapOrHash : public Traits::Bool<false> {}; template <typename T> struct TreatAsMapOrHash : public Traits::Bool<false> {
};
/// \brief \brief The TreatAsMultiMapOrHash class allows treating custom classes as std::multimap or std::unordered_multimap. /// \brief \brief The TreatAsMultiMapOrHash class allows treating custom classes as std::multimap or std::unordered_multimap.
template <typename T> struct TreatAsMultiMapOrHash : public Traits::Bool<false> {}; template <typename T> struct TreatAsMultiMapOrHash : public Traits::Bool<false> {
};
/// \brief \brief The TreatAsSet class allows treating custom classes as std::set or std::unordered_set. /// \brief \brief The TreatAsSet class allows treating custom classes as std::set or std::unordered_set.
template <typename T> struct TreatAsSet : public Traits::Bool<false> {}; template <typename T> struct TreatAsSet : public Traits::Bool<false> {
};
/// \brief \brief The TreatAsMultiSet class allows treating custom classes as std::multiset or std::unordered_multiset. /// \brief \brief The TreatAsMultiSet class allows treating custom classes as std::multiset or std::unordered_multiset.
template <typename T> struct TreatAsMultiSet : public Traits::Bool<false> {}; template <typename T> struct TreatAsMultiSet : public Traits::Bool<false> {
};
#define REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(T) \ #define REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(T) \
template <> struct TreatAsMapOrHash<T> : public Traits::Bool<true> {} template <> struct TreatAsMapOrHash<T> : public Traits::Bool<true> { \
}
#define REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH(T) \ #define REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH(T) \
template <> struct TreatAsMultiMapOrHash<T> : public Traits::Bool<true> {} template <> struct TreatAsMultiMapOrHash<T> : public Traits::Bool<true> { \
}
#define REFLECTIVE_RAPIDJSON_TREAT_AS_SET(T) \ #define REFLECTIVE_RAPIDJSON_TREAT_AS_SET(T) \
template <> struct TreatAsSet<T> : public Traits::Bool<true> {} template <> struct TreatAsSet<T> : public Traits::Bool<true> { \
}
#define REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET(T) \ #define REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET(T) \
template <> struct TreatAsMultiSet<T> : public Traits::Bool<true> {} template <> struct TreatAsMultiSet<T> : public Traits::Bool<true> { \
}
// define traits to check for arrays, sets and maps // define traits to check for arrays, sets and maps
template <typename Type> template <typename Type>

View File

@ -25,11 +25,12 @@ public
#endif #endif
CPP_UTILITIES_TRAITS_DEFINE_TYPE_CHECK(IsVersioned, T::version); CPP_UTILITIES_TRAITS_DEFINE_TYPE_CHECK(IsVersioned, T::version);
//CPP_UTILITIES_TRAITS_DEFINE_TYPE_CHECK(IsVersioned, T::versioningEnabled(std::declval<T &>()));
template <typename VersionType> struct VersionNotSupported { //using BinaryVersion = std::uint64_t;
VersionType presentVersion = 0, maxVersion = 0;
const char *record = nullptr; //template <typename Type, BinaryVersion v = 0> struct BinarySerializable;
}; //CPP_UTILITIES_TRAITS_DEFINE_TYPE_CHECK(IsVersioned, static_cast<T &>(std::declval<T &>()).version);
template <typename Type, bool Condition = IsVersioned<Type>::value> struct Versioning { template <typename Type, bool Condition = IsVersioned<Type>::value> struct Versioning {
static constexpr auto enabled = false; static constexpr auto enabled = false;
@ -38,21 +39,10 @@ template <typename Type, bool Condition = IsVersioned<Type>::value> struct Versi
template <typename Type> struct Versioning<Type, true> { template <typename Type> struct Versioning<Type, true> {
static constexpr auto enabled = Type::version != 0; static constexpr auto enabled = Type::version != 0;
static constexpr auto serializationDefault = Type::version; static constexpr auto serializationDefault = Type::version;
static constexpr auto maxSupported = Type::version;
static constexpr auto applyDefault(decltype(serializationDefault) version) static constexpr auto applyDefault(decltype(serializationDefault) version)
{ {
return version ? version : serializationDefault; return version ? version : serializationDefault;
} }
static constexpr auto isSupported(decltype(maxSupported) version)
{
return version <= maxSupported;
}
static constexpr auto assertVersion(decltype(maxSupported) version, const char *record = nullptr)
{
if (!isSupported(version)) {
throw typename Type::VersionNotSupported({ .presentVersion = version, .maxVersion = maxSupported, .record = record });
}
}
}; };
} // namespace ReflectiveRapidJSON } // namespace ReflectiveRapidJSON