Compare commits
4 Commits
master
...
wip/versio
Author | SHA1 | Date |
---|---|---|
Martchus | 2138491ee5 | |
Martchus | db0082c325 | |
Martchus | 2c6c63579b | |
Martchus | 2c243c3e41 |
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||
|
||||
# set metadata
|
||||
project(reflective_rapidjson)
|
||||
|
|
62
README.md
62
README.md
|
@ -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 |
|
||||
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | 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::variant` | object |
|
||||
| `JsonSerializable` | object |
|
||||
|
||||
### 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.
|
||||
* 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
|
||||
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.
|
||||
It can also be `null` for null pointers or `std::optional` without value.
|
||||
* The JSON type for smart pointer depends on the type the pointer refers to. It can also be `null`.
|
||||
* 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
|
||||
`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
|
||||
`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 <reflective_rapidjson/binary/serializable.h>
|
||||
|
||||
// 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<Nested> { //
|
||||
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 >= 3
|
||||
};
|
||||
|
||||
// example struct where version is serialized/deserialized; defaults to version 3 when writing
|
||||
struct Example : public BinarySerializable<Example, 3> {
|
||||
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 <= 2
|
||||
|
||||
as_of_version(3):
|
||||
std::uint32_t e, f; // will be read/written if version is >= 3
|
||||
|
||||
as_of_version(4):
|
||||
std::uint32_t g; // will be read/written if version is >= 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
|
||||
* Static member variables and member functions are currently ignored by the generator.
|
||||
* It is currently not possible to ignore a specific member variable.
|
||||
|
@ -511,7 +462,7 @@ make
|
|||
make check
|
||||
# build tests but do not run them (optional, requires CppUnit)
|
||||
make tests
|
||||
# generate API documentation (optional, requires Doxygen)
|
||||
# generate API documentation (optional, reqquires Doxygen)
|
||||
make apidoc
|
||||
# install header files, libraries and generator
|
||||
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
|
||||
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).
|
||||
|
|
2
TODOs.md
2
TODOs.md
|
@ -29,5 +29,5 @@
|
|||
- [x] Support `std::unique_ptr` and `std::shared_ptr`
|
||||
- [x] Support `std::map` and `std::unordered_map`
|
||||
- [ ] 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)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||
|
||||
# metadata
|
||||
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
|
||||
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(BasicConfig)
|
||||
include(WindowsResources)
|
||||
|
|
|
@ -246,10 +246,6 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
|
|||
// print writeCustomType method
|
||||
os << "template <> " << visibility << " void writeCustomType<::" << relevantClass.qualifiedName
|
||||
<< ">(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()) {
|
||||
os << " // write version\n"
|
||||
" using V = Versioning<"
|
||||
|
@ -257,8 +253,14 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
|
|||
<< ">;\n"
|
||||
" if constexpr (V::enabled) {\n"
|
||||
" serializer.writeVariableLengthUIntBE(V::applyDefault(version));\n"
|
||||
" } else {\n"
|
||||
" (void)version;\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";
|
||||
auto mt = MemberTracking();
|
||||
for (clang::Decl *const decl : relevantClass.record->decls()) {
|
||||
|
@ -293,7 +295,7 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
|
|||
}
|
||||
mt.concludeCondition(os);
|
||||
if (relevantBases.empty() && !mt.membersWritten) {
|
||||
os << " (void)serializer;\n (void)customObject;\n \n(void)version;";
|
||||
os << " (void)serializer;\n (void)customObject;\n";
|
||||
}
|
||||
os << "}\n";
|
||||
|
||||
|
@ -305,22 +307,20 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
|
|||
// print readCustomType method
|
||||
mt = MemberTracking();
|
||||
os << "template <> " << visibility << " BinaryVersion readCustomType<::" << relevantClass.qualifiedName
|
||||
<< ">(BinaryDeserializer &deserializer, ::" << relevantClass.qualifiedName << " &customObject, BinaryVersion version)\n{\n";
|
||||
os << " // read base classes\n";
|
||||
for (const RelevantClass *baseClass : relevantBases) {
|
||||
os << " deserializer.read(static_cast<::" << baseClass->qualifiedName << " &>(customObject), version);\n";
|
||||
}
|
||||
<< ">(BinaryDeserializer &deserializer, ::" << relevantClass.qualifiedName << " &customObject)\n{\n";
|
||||
if (!relevantClass.relevantBase.empty()) {
|
||||
os << " // read version\n"
|
||||
" using V = Versioning<"
|
||||
" auto version = BinaryVersion();\n"
|
||||
" if constexpr (Versioning<"
|
||||
<< relevantClass.relevantBase
|
||||
<< ">;\n"
|
||||
" if constexpr (V::enabled) {\n"
|
||||
" V::assertVersion(version = deserializer.readVariableLengthUIntBE(), \""
|
||||
<< relevantClass.qualifiedName
|
||||
<< "\");\n"
|
||||
<< ">::enabled) {\n"
|
||||
" version = deserializer.readVariableLengthUIntBE();\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";
|
||||
for (clang::Decl *const decl : relevantClass.record->decls()) {
|
||||
// check static member variables for version markers
|
||||
|
@ -344,7 +344,7 @@ void BinarySerializationCodeGenerator::generate(std::ostream &os) const
|
|||
mt.writeExtraPadding(os);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
|||
}
|
||||
}
|
||||
if (relevantBases.empty() && !pushWritten) {
|
||||
os << " (void)reflectable;\n (void)value;\n (void)allocator;\n";
|
||||
os << " (void)reflectable;\n (void)value;\n";
|
||||
}
|
||||
os << "}\n";
|
||||
|
||||
|
@ -146,7 +146,7 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
|
|||
os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
|
||||
}
|
||||
os << " // set error context for current record\n"
|
||||
" const char *previousRecord = nullptr;\n"
|
||||
" const char *previousRecord;\n"
|
||||
" if (errors) {\n"
|
||||
" previousRecord = errors->currentRecord;\n"
|
||||
" errors->currentRecord = \""
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include <clang/AST/DeclCXX.h>
|
||||
#include <clang/AST/DeclFriend.h>
|
||||
#include <clang/AST/DeclTemplate.h>
|
||||
#include <clang/AST/PrettyPrinter.h>
|
||||
#include <clang/AST/QualTypeNames.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -96,13 +94,12 @@ void SerializationCodeGenerator::computeRelevantClass(RelevantClass &possiblyRel
|
|||
|
||||
// consider all classes inheriting from an instantiation of "JsonSerializable" relevant
|
||||
if (const auto *const relevantBase = inheritsFromInstantiationOf(possiblyRelevantClass.record, m_qualifiedNameOfRecords)) {
|
||||
auto policy = clang::PrintingPolicy(possiblyRelevantClass.record->getASTContext().getLangOpts());
|
||||
policy.FullyQualifiedName = true;
|
||||
policy.SuppressScope = false;
|
||||
policy.SuppressUnwrittenScope = false;
|
||||
policy.SplitTemplateClosers = false;
|
||||
possiblyRelevantClass.relevantBase
|
||||
= clang::TypeName::getFullyQualifiedName(relevantBase->getType(), possiblyRelevantClass.record->getASTContext(), policy, true);
|
||||
cerr << "record: " << possiblyRelevantClass.qualifiedName << '\n';
|
||||
for (const clang::CXXBaseSpecifier base : possiblyRelevantClass.record->bases()) {
|
||||
cerr << "base: " << base.getType().getAsString() << '\n';
|
||||
}
|
||||
cerr << "relevant base: " << relevantBase->getType().getAsString();
|
||||
possiblyRelevantClass.relevantBase = relevantBase->getType().getAsString();
|
||||
possiblyRelevantClass.isRelevant = IsRelevant::Yes;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ template <> void pull<::TestNamespace1::Person>(::TestNamespace1::Person &refle
|
|||
{
|
||||
// pull base classes
|
||||
// set error context for current record
|
||||
const char *previousRecord = nullptr;
|
||||
const char *previousRecord;
|
||||
if (errors) {
|
||||
previousRecord = errors->currentRecord;
|
||||
errors->currentRecord = "TestNamespace1::Person";
|
||||
|
@ -39,7 +39,7 @@ template <> void pull<::TestNamespace2::ThirdPartyStruct>(::TestNamespace2::Thi
|
|||
{
|
||||
// pull base classes
|
||||
// set error context for current record
|
||||
const char *previousRecord = nullptr;
|
||||
const char *previousRecord;
|
||||
if (errors) {
|
||||
previousRecord = errors->currentRecord;
|
||||
errors->currentRecord = "TestNamespace2::ThirdPartyStruct";
|
||||
|
|
|
@ -70,11 +70,6 @@ struct PointerStruct : public BinarySerializable<PointerStruct> {
|
|||
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.
|
||||
*/
|
||||
|
@ -93,6 +88,4 @@ as_of_version(4):
|
|||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace SomeNamespace
|
||||
|
||||
#endif // REFLECTIVE_RAPIDJSON_TESTS_MORE_STRUCTS_H
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||
|
||||
# metadata
|
||||
set(META_PROJECT_TYPE library)
|
||||
|
|
|
@ -24,17 +24,19 @@
|
|||
namespace ReflectiveRapidJSON {
|
||||
namespace BinaryReflector {
|
||||
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *>
|
||||
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version)
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *>
|
||||
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
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
namespace ReflectiveRapidJSON {
|
||||
namespace BinaryReflector {
|
||||
|
||||
template <>
|
||||
inline BinaryVersion readCustomType<CppUtilities::DateTime>(BinaryDeserializer &deserializer, CppUtilities::DateTime &dateTime, BinaryVersion version)
|
||||
template <> inline BinaryVersion readCustomType<CppUtilities::DateTime>(BinaryDeserializer &deserializer, CppUtilities::DateTime &dateTime)
|
||||
{
|
||||
CPP_UTILITIES_UNUSED(version)
|
||||
deserializer.read(dateTime.ticks());
|
||||
return 0;
|
||||
}
|
||||
|
@ -31,10 +29,8 @@ inline void writeCustomType<CppUtilities::DateTime>(BinarySerializer &serializer
|
|||
serializer.write(dateTime.totalTicks());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline BinaryVersion readCustomType<CppUtilities::TimeSpan>(BinaryDeserializer &deserializer, CppUtilities::TimeSpan &timeSpan, BinaryVersion version)
|
||||
template <> inline BinaryVersion readCustomType<CppUtilities::TimeSpan>(BinaryDeserializer &deserializer, CppUtilities::TimeSpan &timeSpan)
|
||||
{
|
||||
CPP_UTILITIES_UNUSED(version)
|
||||
deserializer.read(timeSpan.ticks());
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
#include "../traits.h"
|
||||
#include "../versioning.h"
|
||||
|
||||
#include <c++utilities/conversion/conversionexception.h>
|
||||
#include <c++utilities/io/binaryreader.h>
|
||||
|
@ -17,7 +16,6 @@
|
|||
#include <any>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
|
@ -36,6 +34,7 @@ template <typename T> struct AdaptedBinarySerializable : public Traits::Bool<fal
|
|||
};
|
||||
|
||||
using BinaryVersion = std::uint64_t;
|
||||
|
||||
template <typename Type, BinaryVersion v = 0> struct BinarySerializable;
|
||||
|
||||
/*!
|
||||
|
@ -47,29 +46,17 @@ namespace BinaryReflector {
|
|||
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,
|
||||
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>,
|
||||
IsVariant<Type>>;
|
||||
Traits::IsIteratable<Type>, Traits::IsSpecializingAnyOf<Type, std::pair, std::unique_ptr, std::shared_ptr>, std::is_enum<Type>, IsVariant<Type>>;
|
||||
template <typename Type> using IsCustomType = Traits::Not<IsBuiltInType<Type>>;
|
||||
|
||||
class BinaryDeserializer;
|
||||
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>
|
||||
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType, BinaryVersion version = 0);
|
||||
|
||||
/// \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.
|
||||
BinaryVersion readCustomType(BinaryDeserializer &deserializer, Type &customType);
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr>
|
||||
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 {
|
||||
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::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::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::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr> void read(Type &iteratable);
|
||||
template <typename Type,
|
||||
|
@ -89,14 +75,12 @@ public:
|
|||
void read(Type &iteratable);
|
||||
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<IsBuiltInType<Type>> * = nullptr> BinaryVersion read(Type &builtInType, BinaryVersion version);
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> BinaryVersion read(Type &customType, BinaryVersion version = 0);
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> * = nullptr> BinaryVersion read(Type &customType);
|
||||
|
||||
private:
|
||||
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 {
|
||||
friend class ::BinaryReflectorTests;
|
||||
|
||||
|
@ -105,8 +89,7 @@ public:
|
|||
|
||||
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::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> * = nullptr>
|
||||
void write(const Type &pointer);
|
||||
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_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<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)
|
||||
{
|
||||
const auto size = readVariableLengthUIntBE();
|
||||
|
@ -239,15 +212,9 @@ template <typename Type, Traits::EnableIf<IsVariant<Type>> *> void BinaryDeseria
|
|||
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 version;
|
||||
}
|
||||
|
||||
template <typename Type, Traits::EnableIf<IsCustomType<Type>> *> BinaryVersion BinaryDeserializer::read(Type &customType, BinaryVersion version)
|
||||
{
|
||||
return readCustomType(*this, customType, version);
|
||||
return readCustomType(*this, customType);
|
||||
}
|
||||
|
||||
inline BinarySerializer::BinarySerializer(std::ostream *stream)
|
||||
|
@ -261,12 +228,12 @@ template <typename Type, Traits::EnableIf<Traits::IsSpecializationOf<Type, std::
|
|||
write(pair.second);
|
||||
}
|
||||
|
||||
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr, std::optional>> *>
|
||||
void BinarySerializer::write(const Type &opt)
|
||||
template <typename Type, Traits::EnableIf<Traits::IsSpecializingAnyOf<Type, std::unique_ptr>> *> void BinarySerializer::write(const Type &pointer)
|
||||
{
|
||||
writeBool(static_cast<bool>(opt));
|
||||
if (opt) {
|
||||
write(*opt);
|
||||
const bool hasValue = pointer != nullptr;
|
||||
writeBool(hasValue);
|
||||
if (hasValue) {
|
||||
write(*pointer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,23 +14,20 @@
|
|||
|
||||
namespace ReflectiveRapidJSON {
|
||||
|
||||
using BinaryVersionNotSupported = VersionNotSupported<BinaryVersion>;
|
||||
|
||||
/*!
|
||||
* \brief The BinarySerializable class provides the CRTP-base for (de)serializable objects.
|
||||
*/
|
||||
template <typename Type, BinaryVersion v> struct BinarySerializable {
|
||||
using VersionNotSupported = BinaryVersionNotSupported;
|
||||
void toBinary(std::ostream &outputStream, BinaryVersion version = 0) const;
|
||||
BinaryVersion restoreFromBinary(std::istream &inputStream);
|
||||
static Type fromBinary(std::istream &inputStream);
|
||||
|
||||
static constexpr const char *qualifiedName = "ReflectiveRapidJSON::BinarySerializable";
|
||||
static constexpr auto version = v;
|
||||
|
||||
#if __cplusplus > 201707L
|
||||
bool operator==(const BinarySerializable<Type, v> &) const = default;
|
||||
#endif
|
||||
static constexpr auto versioningEnabled(const BinarySerializable<Type, v> &)
|
||||
{
|
||||
return v != 0;
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
*/
|
||||
#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
|
||||
|
||||
|
|
|
@ -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
|
||||
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")
|
||||
endif ()
|
||||
endif ()
|
||||
execute_process(
|
||||
COMMAND ${REFLECTION_GENERATOR_CLANG_BIN} -print-resource-dir
|
||||
OUTPUT_VARIABLE REFLECTION_GENERATOR_CLANG_RESOURCE_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
exec_program(
|
||||
${REFLECTION_GENERATOR_CLANG_BIN} ARGS
|
||||
-print-resource-dir
|
||||
OUTPUT_VARIABLE REFLECTION_GENERATOR_CLANG_RESOURCE_DIR)
|
||||
endif ()
|
||||
if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR OR NOT IS_DIRECTORY "${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR}")
|
||||
message(
|
||||
|
@ -87,7 +87,7 @@ endfunction ()
|
|||
include(CMakeParseArguments)
|
||||
function (add_reflection_generator_invocation)
|
||||
# parse arguments
|
||||
set(OPTIONAL_ARGS ERROR_RESILIENT)
|
||||
set(OPTIONAL_ARGS)
|
||||
set(ONE_VALUE_ARGS OUTPUT_DIRECTORY JSON_VISIBILITY BINARY_VISBILITY)
|
||||
set(MULTI_VALUE_ARGS
|
||||
INPUT_FILES
|
||||
|
@ -121,25 +121,30 @@ function (add_reflection_generator_invocation)
|
|||
list(APPEND ARGS_CLANG_OPTIONS -I "${INCLUDE_DIR}")
|
||||
endforeach ()
|
||||
|
||||
# avoid including headers from host when cross compiling
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
list(APPEND ARGS_CLANG_OPTIONS -nostdinc)
|
||||
# add workaround for cross compiling with mingw-w64 to prevent host stdlib.h being included (not sure why specifying
|
||||
# REFLECTION_GENERATOR_INCLUDE_DIRECTORIES is not enough to let it find this particular header file)
|
||||
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 ()
|
||||
|
||||
# add options to be passed to clang from the specified targets
|
||||
if (ARGS_CLANG_OPTIONS_FROM_TARGETS)
|
||||
foreach (TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_TARGETS})
|
||||
# set c++ standard
|
||||
list(
|
||||
APPEND
|
||||
ARGS_CLANG_OPTIONS
|
||||
"$<$<BOOL:$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>:-std=c++$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>"
|
||||
)
|
||||
# add compile flags and options
|
||||
list(APPEND ARGS_CLANG_OPTIONS "-std=c++$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>")
|
||||
# add compile flags
|
||||
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_FLAGS)
|
||||
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
|
||||
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_DEFINITIONS)
|
||||
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)
|
||||
list(APPEND CLI_ARGUMENTS --binary-visibility "${ARGS_BINARY_VISBILITY}")
|
||||
endif ()
|
||||
if (ARGS_ERROR_RESILIENT)
|
||||
list(APPEND CLI_ARGUMENTS --error-resilient)
|
||||
endif ()
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTPUT_FILE}"
|
||||
COMMAND "${REFLECTION_GENERATOR_EXECUTABLE}" ARGS ${CLI_ARGUMENTS}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../versioning.h
|
|
@ -155,7 +155,6 @@ struct JsonDeserializationErrors : public std::vector<JsonDeserializationError>
|
|||
JsonDeserializationErrors();
|
||||
|
||||
template <typename ExpectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType);
|
||||
template <RAPIDJSON_NAMESPACE::Type expectedType> void reportTypeMismatch(RAPIDJSON_NAMESPACE::Type presentType);
|
||||
void reportArraySizeMismatch();
|
||||
void reportConversionError(JsonType jsonType);
|
||||
void reportUnexpectedDuplicate(JsonType jsonType);
|
||||
|
@ -221,16 +220,6 @@ template <typename ExpectedType> inline void JsonDeserializationErrors::reportTy
|
|||
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.
|
||||
* \todo Allow specifying expected and actual size.
|
||||
|
|
|
@ -19,13 +19,11 @@
|
|||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#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
|
||||
template <typename 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::IsSpecializingAnyOf<Type, std::unique_ptr, std::shared_ptr, std::weak_ptr, std::optional>, IsVariant<Type>>;
|
||||
Traits::IsSpecializationOf<Type, std::tuple>, Traits::IsSpecializationOf<Type, std::pair>, Traits::IsIteratable<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>>;
|
||||
|
||||
// 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.
|
||||
*/
|
||||
template <typename Type,
|
||||
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>
|
||||
template <typename Type, Traits::EnableIfAny<std::is_integral<Type>, std::is_floating_point<Type>> * = nullptr>
|
||||
inline void push(Type reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &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.
|
||||
*/
|
||||
|
@ -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,
|
||||
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)
|
||||
{
|
||||
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>
|
||||
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.
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
template <typename Type,
|
||||
Traits::EnableIf<Traits::Not<std::is_same<Type, bool>>, Traits::Not<std::is_same<Type, std::uint8_t>>,
|
||||
Traits::Not<std::is_same<Type, std::int8_t>>, Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>>> * = nullptr>
|
||||
Traits::EnableIf<Traits::Not<std::is_same<Type, bool>>, Traits::Any<std::is_integral<Type>, std::is_floating_point<Type>>> * = nullptr>
|
||||
inline void pull(
|
||||
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());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \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.
|
||||
*/
|
||||
|
@ -701,7 +668,7 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
|
|||
++index;
|
||||
typename Type::value_type itemObj;
|
||||
pull(itemObj, item, errors);
|
||||
reflectable.emplace(std::move(itemObj));
|
||||
reflectable.emplace(move(itemObj));
|
||||
}
|
||||
|
||||
// clear error context
|
||||
|
@ -729,7 +696,7 @@ void pull(Type &reflectable, rapidjson::GenericValue<RAPIDJSON_NAMESPACE::UTF8<c
|
|||
++index;
|
||||
typename Type::value_type itemObj;
|
||||
pull(itemObj, item, errors);
|
||||
if (!reflectable.emplace(std::move(itemObj)).second) {
|
||||
if (!reflectable.emplace(move(itemObj)).second) {
|
||||
errors->reportUnexpectedDuplicate(JsonType::Array);
|
||||
}
|
||||
}
|
||||
|
@ -882,20 +849,6 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
|
|||
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
|
||||
namespace Detail {
|
||||
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
|
||||
const char *previousMember = nullptr;
|
||||
const char *previousMember;
|
||||
if (errors) {
|
||||
previousMember = errors->currentMember;
|
||||
errors->currentMember = name;
|
||||
|
|
|
@ -31,10 +31,6 @@ template <typename Type> struct JsonSerializable {
|
|||
static Type fromJson(const std::string &json, JsonDeserializationErrors *errors = nullptr);
|
||||
|
||||
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.
|
||||
*/
|
||||
#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.
|
||||
|
|
|
@ -75,9 +75,8 @@ struct ObjectWithVariantsBinary : public BinarySerializable<ObjectWithVariantsBi
|
|||
namespace ReflectiveRapidJSON {
|
||||
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.number2);
|
||||
deserializer.read(customType.numbers);
|
||||
|
@ -116,9 +115,8 @@ template <> void writeCustomType<TestObjectBinary>(BinarySerializer &serializer,
|
|||
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.testObjects);
|
||||
return 0;
|
||||
|
@ -126,15 +124,12 @@ template <> BinaryVersion readCustomType<NestingArrayBinary>(BinaryDeserializer
|
|||
|
||||
template <> void writeCustomType<NestingArrayBinary>(BinarySerializer &serializer, const NestingArrayBinary &customType, BinaryVersion version)
|
||||
{
|
||||
CPP_UTILITIES_UNUSED(version)
|
||||
serializer.write(customType.name);
|
||||
serializer.write(customType.testObjects);
|
||||
}
|
||||
|
||||
template <>
|
||||
BinaryVersion readCustomType<ObjectWithVariantsBinary>(BinaryDeserializer &deserializer, ObjectWithVariantsBinary &customType, BinaryVersion version)
|
||||
template <> BinaryVersion readCustomType<ObjectWithVariantsBinary>(BinaryDeserializer &deserializer, ObjectWithVariantsBinary &customType)
|
||||
{
|
||||
CPP_UTILITIES_UNUSED(version)
|
||||
deserializer.read(customType.someVariant);
|
||||
deserializer.read(customType.anotherVariant);
|
||||
deserializer.read(customType.yetAnotherVariant);
|
||||
|
@ -170,7 +165,6 @@ class BinaryReflectorTests : public TestFixture {
|
|||
CPPUNIT_TEST(testSmallSharedPointer);
|
||||
CPPUNIT_TEST(testBigSharedPointer);
|
||||
CPPUNIT_TEST(testVariant);
|
||||
CPPUNIT_TEST(testOptional);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -188,7 +182,6 @@ public:
|
|||
void testSmallSharedPointer();
|
||||
void testBigSharedPointer();
|
||||
void testVariant();
|
||||
void testOptional();
|
||||
|
||||
private:
|
||||
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()
|
||||
{
|
||||
stringstream stream(ios_base::out | ios_base::binary);
|
||||
stream.exceptions(ios_base::failbit | ios_base::badbit);
|
||||
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);
|
||||
readBuffer(stream, m_buffer.data(), m_buffer.size());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(m_expectedTestObj, m_buffer);
|
||||
}
|
||||
|
@ -319,7 +281,7 @@ void BinaryReflectorTests::testDeserializeSimpleStruct()
|
|||
{
|
||||
stringstream stream(ios_base::in | ios_base::binary);
|
||||
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));
|
||||
assertTestObject(deserialized);
|
||||
}
|
||||
|
@ -329,9 +291,8 @@ void BinaryReflectorTests::testSerializeNestedStruct()
|
|||
stringstream stream(ios_base::out | ios_base::binary);
|
||||
stream.exceptions(ios_base::failbit | ios_base::badbit);
|
||||
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);
|
||||
readBuffer(stream, m_buffer.data(), m_buffer.size());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(m_expectedNestedTestObj, m_buffer);
|
||||
}
|
||||
|
@ -340,7 +301,7 @@ void BinaryReflectorTests::testDeserializeNestedStruct()
|
|||
{
|
||||
stringstream stream(ios_base::in | ios_base::binary);
|
||||
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));
|
||||
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(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());
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ template <> inline void push<NestingArray>(const NestingArray &reflectable, Valu
|
|||
template <>
|
||||
inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
|
||||
{
|
||||
const char *previousRecord = nullptr;
|
||||
const char *previousRecord;
|
||||
if (errors) {
|
||||
previousRecord = errors->currentRecord;
|
||||
errors->currentRecord = "TestObject";
|
||||
|
@ -141,7 +141,7 @@ inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<ch
|
|||
template <>
|
||||
inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
|
||||
{
|
||||
const char *previousRecord = nullptr;
|
||||
const char *previousRecord;
|
||||
if (errors) {
|
||||
previousRecord = errors->currentRecord;
|
||||
errors->currentRecord = "NestingObject";
|
||||
|
@ -156,7 +156,7 @@ inline void pull<NestingObject>(NestingObject &reflectable, const GenericValue<U
|
|||
template <>
|
||||
inline void pull<NestingArray>(NestingArray &reflectable, const GenericValue<UTF8<char>>::ConstObject &value, JsonDeserializationErrors *errors)
|
||||
{
|
||||
const char *previousRecord = nullptr;
|
||||
const char *previousRecord;
|
||||
if (errors) {
|
||||
previousRecord = errors->currentRecord;
|
||||
errors->currentRecord = "NestingArray";
|
||||
|
@ -186,13 +186,11 @@ class JsonReflectorTests : public TestFixture {
|
|||
CPPUNIT_TEST(testSerializeNestedObjects);
|
||||
CPPUNIT_TEST(testSerializeUniquePtr);
|
||||
CPPUNIT_TEST(testSerializeSharedPtr);
|
||||
CPPUNIT_TEST(testSerializeOptional);
|
||||
CPPUNIT_TEST(testDeserializePrimitives);
|
||||
CPPUNIT_TEST(testDeserializeSimpleObjects);
|
||||
CPPUNIT_TEST(testDeserializeNestedObjects);
|
||||
CPPUNIT_TEST(testDeserializeUniquePtr);
|
||||
CPPUNIT_TEST(testDeserializeSharedPtr);
|
||||
CPPUNIT_TEST(testDeserializeOptional);
|
||||
CPPUNIT_TEST(testHandlingParseError);
|
||||
CPPUNIT_TEST(testHandlingTypeMismatch);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
@ -207,13 +205,11 @@ public:
|
|||
void testSerializeNestedObjects();
|
||||
void testSerializeUniquePtr();
|
||||
void testSerializeSharedPtr();
|
||||
void testSerializeOptional();
|
||||
void testDeserializePrimitives();
|
||||
void testDeserializeSimpleObjects();
|
||||
void testDeserializeNestedObjects();
|
||||
void testDeserializeUniquePtr();
|
||||
void testDeserializeSharedPtr();
|
||||
void testDeserializeOptional();
|
||||
void testHandlingParseError();
|
||||
void testHandlingTypeMismatch();
|
||||
|
||||
|
@ -383,28 +379,6 @@ void JsonReflectorTests::testSerializeSharedPtr()
|
|||
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.
|
||||
*/
|
||||
|
@ -543,9 +517,6 @@ void JsonReflectorTests::testDeserializeNestedObjects()
|
|||
CPPUNIT_ASSERT_EQUAL("test"s, nestedInVector[0].text);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Tests deserializing std::optional.
|
||||
*/
|
||||
void JsonReflectorTests::testDeserializeUniquePtr()
|
||||
{
|
||||
Document doc(kArrayType);
|
||||
|
@ -590,22 +561,6 @@ void JsonReflectorTests::testDeserializeSharedPtr()
|
|||
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().
|
||||
*/
|
||||
|
|
|
@ -7,14 +7,20 @@
|
|||
#include <vector>
|
||||
|
||||
// define structs for testing REFLECTIVE_RAPIDJSON_TREAT_AS_…
|
||||
struct Foo {};
|
||||
struct Bar {};
|
||||
struct Foo {
|
||||
};
|
||||
struct Bar {
|
||||
};
|
||||
|
||||
// define structs for testing versioning
|
||||
struct VersionlessBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase> {};
|
||||
struct VersionedDerived : public VersionlessBase, public ReflectiveRapidJSON::BinarySerializable<VersionedDerived, 1> {};
|
||||
struct VersionedBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase, 1> {};
|
||||
struct VersionlessDerived : public VersionedBase, public ReflectiveRapidJSON::BinarySerializable<VersionlessDerived> {};
|
||||
struct VersionlessBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase> {
|
||||
};
|
||||
struct VersionedDerived : public VersionlessBase, public ReflectiveRapidJSON::BinarySerializable<VersionedDerived, 1> {
|
||||
};
|
||||
struct VersionedBase : public ReflectiveRapidJSON::BinarySerializable<VersionlessBase, 1> {
|
||||
};
|
||||
struct VersionlessDerived : public VersionedBase, public ReflectiveRapidJSON::BinarySerializable<VersionlessDerived> {
|
||||
};
|
||||
|
||||
namespace ReflectiveRapidJSON {
|
||||
REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH(Foo);
|
||||
|
|
24
lib/traits.h
24
lib/traits.h
|
@ -16,22 +16,30 @@ namespace Traits = ::CppUtilities::Traits;
|
|||
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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) \
|
||||
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) \
|
||||
template <> struct TreatAsMultiMapOrHash<T> : public Traits::Bool<true> {}
|
||||
template <> struct TreatAsMultiMapOrHash<T> : public Traits::Bool<true> { \
|
||||
}
|
||||
#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) \
|
||||
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
|
||||
template <typename Type>
|
||||
|
|
|
@ -25,11 +25,12 @@ public
|
|||
#endif
|
||||
|
||||
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 {
|
||||
VersionType presentVersion = 0, maxVersion = 0;
|
||||
const char *record = nullptr;
|
||||
};
|
||||
//using BinaryVersion = std::uint64_t;
|
||||
|
||||
//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 {
|
||||
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> {
|
||||
static constexpr auto enabled = Type::version != 0;
|
||||
static constexpr auto serializationDefault = Type::version;
|
||||
static constexpr auto maxSupported = Type::version;
|
||||
static constexpr auto applyDefault(decltype(serializationDefault) version)
|
||||
{
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue