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
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 |
| 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 &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
* 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).

View File

@ -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)

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
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)

View File

@ -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;
}
}

View File

@ -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 = \""

View File

@ -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;
}

View File

@ -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";

View File

@ -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

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
set(META_PROJECT_TYPE library)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

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
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}

View File

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

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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());
}

View File

@ -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().
*/

View File

@ -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);

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, ...
/// \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>

View File

@ -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