Skip to content

Instantly share code, notes, and snippets.

@meitinger
Last active January 30, 2020 19:46
Show Gist options
  • Save meitinger/fe83b21ae265eda2060f60a38f21a956 to your computer and use it in GitHub Desktop.
Save meitinger/fe83b21ae265eda2060f60a38f21a956 to your computer and use it in GitHub Desktop.
Serialization Framework for RapidJSON
// Copyright (C) 2020 Manuel Meitinger
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_PERSIST_H_
#define RAPIDJSON_PERSIST_H_
#include "document.h"
#include <algorithm>
#include <array>
#include <bitset>
#include <complex>
#include <deque>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <vector>
RAPIDJSON_NAMESPACE_BEGIN
namespace persist {
///////////////////////////////////////////////////////////////////////////////
// unicode
#ifndef RAPIDJSON_PERSIST_UNICODE
#if defined(UNICODE) || defined(_UNICODE)
#define RAPIDJSON_PERSIST_UNICODE 1
#else
#define RAPIDJSON_PERSIST_UNICODE 0
#endif
#endif
///////////////////////////////////////////////////////////////////////////////
// TypedPointers, NamedObjects
using type_info_ref = std::reference_wrapper<const std::type_info>;
struct type_info_ref_hash {
std::size_t operator()(type_info_ref code) const noexcept { return code.get().hash_code(); }
};
struct type_info_ref_equal_to {
bool operator()(type_info_ref lhs, type_info_ref rhs) const noexcept { return lhs.get() == rhs.get(); }
};
using TypedPointers = std::unordered_map<type_info_ref, void *, type_info_ref_hash, type_info_ref_equal_to>;
template <typename Encoding>
using NamedObjects = std::unordered_map<std::basic_string<typename Encoding::Ch>, TypedPointers>;
///////////////////////////////////////////////////////////////////////////////
// macros, primary serializer
#define RAPIDJSON_PERSIST_TEMPLATE_INTERNAL typename Encoding, typename HeapAllocator, typename StackAllocator
#define RAPIDJSON_PERSIST_TEMPLATE template <RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
#define RAPIDJSON_PERSIST_DOCUMENT GenericDocument<Encoding, HeapAllocator, StackAllocator>
#define RAPIDJSON_PERSIST_VALUE GenericValue<Encoding, HeapAllocator>
#define RAPIDJSON_PERSIST_SERIALIZE_INTERNAL(name, ...) \
RAPIDJSON_PERSIST_VALUE name(const __VA_ARGS__ &value, RAPIDJSON_PERSIST_DOCUMENT &doc)
#define RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(name, ...) \
bool name(const RAPIDJSON_PERSIST_VALUE &json, __VA_ARGS__ &value, const RAPIDJSON_PERSIST_DOCUMENT &doc, \
NamedObjects<Encoding> &objects)
#define RAPIDJSON_PERSIST_SERIALIZE(...) \
RAPIDJSON_PERSIST_TEMPLATE static RAPIDJSON_PERSIST_SERIALIZE_INTERNAL(serialize, __VA_ARGS__)
#define RAPIDJSON_PERSIST_DESERIALIZE(...) \
RAPIDJSON_PERSIST_TEMPLATE static RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(deserialize, __VA_ARGS__)
template <typename T, typename Enable = void>
struct serializer {
RAPIDJSON_PERSIST_SERIALIZE(T) { return value.serialize(doc); }
RAPIDJSON_PERSIST_DESERIALIZE(T) { return value.deserialize(json, doc, objects); }
};
///////////////////////////////////////////////////////////////////////////////
// formatters (char*, std::*string, std::bitset)
template <typename T, typename Enable = void>
struct formatter;
template <typename Char>
struct formatter<Char, std::enable_if_t<std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>>> {
template <typename Encoding>
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, Char>, std::basic_string<Char>>
format(const Char &value)
{
return std::basic_string<Char>(&value, 1);
}
template <typename Encoding>
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, Char>, Char>
parse(const std::basic_string<typename Encoding::Ch> &str)
{
if (str.length() != 1) {
throw std::logic_error("single character expected");
}
return str[0];
}
};
template <typename Char, typename Traits, typename Allocator>
struct formatter<std::basic_string<Char, Traits, Allocator>> {
template <typename Encoding>
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, Char>, std::basic_string<Char>>
format(const std::basic_string<Char, Traits, Allocator> &value)
{
return std::basic_string<Char>(value.data(), value.length());
}
template <typename Encoding>
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, Char>, std::basic_string<Char, Traits, Allocator>>
parse(const std::basic_string<Char> &str)
{
return std::basic_string<Char, Traits, Allocator>(str.data(), str.length());
}
};
template <std::size_t N>
struct formatter<std::bitset<N>> {
template <typename Encoding>
static std::basic_string<typename Encoding::Ch> format(const std::bitset<N> &value)
{
using Char = typename Encoding::Ch;
return value.template to_string<Char, std::char_traits<Char>, std::allocator<Char>>();
}
template <typename Encoding>
static std::bitset<N> parse(const std::basic_string<typename Encoding::Ch> &str)
{
return std::bitset<N>(str);
}
};
///////////////////////////////////////////////////////////////////////////////
// literals
template <typename Char>
struct literals;
#define RAPIDJSON_PERIST_LITERALS(type, p) \
template <> \
struct literals<type> { \
static constexpr const auto ref = std::basic_string_view<type>(p##"ref:"); \
static constexpr const auto main = std::basic_string_view<type>(p##"main"); \
static constexpr const auto key = std::basic_string_view<type>(p##"key"); \
static constexpr const auto value = std::basic_string_view<type>(p##"value"); \
static constexpr const auto real = std::basic_string_view<type>(p##"r"); \
static constexpr const auto imag = std::basic_string_view<type>(p##"i"); \
static constexpr const auto sep = std::basic_string_view<type>(p##" | "); \
static constexpr const auto sep_char = p##'|'; \
}
RAPIDJSON_PERIST_LITERALS(char, );
RAPIDJSON_PERIST_LITERALS(wchar_t, L);
#undef RAPIDJSON_PERIST_LITERALS
///////////////////////////////////////////////////////////////////////////////
// fundamental types
#define RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE(type, conv_type, name) \
template <> \
struct serializer<type> { \
RAPIDJSON_PERSIST_SERIALIZE(type) { return RAPIDJSON_PERSIST_VALUE(static_cast<conv_type>(value)); } \
RAPIDJSON_PERSIST_DESERIALIZE(type) \
{ \
if (!json.Is##name()) { \
return false; \
} \
const auto raw_value = json.Get##name(); \
if constexpr (!std::is_same_v<decltype(raw_value), type>) { \
if (raw_value < std::numeric_limits<type>::min() || raw_value > std::numeric_limits<type>::max()) { \
return false; \
} \
} \
value = static_cast<type>(raw_value); \
return true; \
} \
}
#define RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(type, conv_type, name, parser) \
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE(type, conv_type, name); \
template <> \
struct formatter<type> { \
template <typename Encoding> \
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, char>, std::string> format(const type &value) \
{ \
return std::to_string(static_cast<conv_type>(value)); \
} \
template <typename Encoding> \
static std::enable_if_t<std::is_same_v<typename Encoding::Ch, wchar_t>, std::wstring> \
format(const type &value) \
{ \
return std::to_wstring(static_cast<conv_type>(value)); \
} \
template <typename Encoding> \
static type parse(const std::basic_string<typename Encoding::Ch> &str) \
{ \
const auto value = parser(str); \
if (value < std::numeric_limits<type>::min() || value > std::numeric_limits<type>::max()) { \
throw std::out_of_range(#type " value is out of bounds"); \
} \
return static_cast<type>(value); \
} \
}
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE(bool, bool, Bool);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(signed char, int, Int, std::stoi);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(unsigned char, unsigned int, Uint, std::stoul);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(short int, int, Int, std::stoi);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(unsigned short int, unsigned int, Uint, std::stoul);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(int, int, Int, std::stoi);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(unsigned int, unsigned int, Uint, std::stoul);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(long int, int64_t, Int64, std::stol);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(unsigned long int, uint64_t, Uint64, std::stoul);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(long long int, int64_t, Int64, std::stoll);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(unsigned long long int, uint64_t, Uint64, std::stoull);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(float, float, Float, std::stof);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(double, double, Double, std::stod);
RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE(long double, double, Double, std::stold);
#undef RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE
#undef RAPIDJSON_PERSIST_FUNDAMENTAL_TYPE_FORMATTABLE
///////////////////////////////////////////////////////////////////////////////
// formattable
template <typename T>
struct serializer<T, std::enable_if_t<std::is_constructible_v<formatter<T>>>> {
RAPIDJSON_PERSIST_SERIALIZE(T)
{
const auto str = formatter<T>::template format<Encoding>(value);
return RAPIDJSON_PERSIST_VALUE(str.data(), str.length(), doc.GetAllocator());
}
RAPIDJSON_PERSIST_DESERIALIZE(T)
{
if (!json.IsString()) {
return false;
}
try {
value = formatter<T>::template parse<Encoding>(
std::basic_string<typename Encoding::Ch>(json.GetString(), json.GetStringLength()));
} catch (const std::logic_error &) {
return false;
}
return true;
}
};
///////////////////////////////////////////////////////////////////////////////
// std::shared_ptr
template <typename T>
struct serializer<std::shared_ptr<T>> {
RAPIDJSON_PERSIST_SERIALIZE(std::shared_ptr<T>)
{
using Char = typename Encoding::Ch;
if (!value) {
return RAPIDJSON_PERSIST_VALUE();
}
const auto name = formatter<uintptr_t>::template format<Encoding>(reinterpret_cast<uintptr_t>(value.get()));
if (doc.FindMember(StringRef(name.data(), name.size())) == doc.MemberEnd()) {
doc.AddMember(RAPIDJSON_PERSIST_VALUE(name.data(), name.size(), doc.GetAllocator()),
serializer<T>::serialize(*value, doc), doc.GetAllocator());
}
const auto ref_name =
std::basic_string<Char>(literals<Char>::ref).append(std::basic_string_view(name.data(), name.size()));
return RAPIDJSON_PERSIST_VALUE(ref_name.data(), ref_name.length(), doc.GetAllocator());
}
RAPIDJSON_PERSIST_DESERIALIZE(std::shared_ptr<T>)
{
using Char = typename Encoding::Ch;
// reset on null value
if (json.IsNull()) {
value.reset();
return true;
}
// get the name part from the "ref:<name>" value
if (!json.IsString()) {
return false;
}
const auto ref_name = std::basic_string_view(json.GetString(), json.GetStringLength());
if (ref_name.compare(0, literals<Char>::ref.length(), literals<Char>::ref) != 0) {
return false;
}
const auto name = std::basic_string<Char>(ref_name.substr(literals<Char>::ref.length()));
// return a deserialized shared_ptr, if any
const auto &type_id = typeid(std::shared_ptr<T>);
const auto type_ptrs_entry = objects.find(name);
if (type_ptrs_entry != objects.end()) {
const auto ptr_entry = type_ptrs_entry->second.find(type_id);
if (ptr_entry != type_ptrs_entry->second.end()) {
value = *reinterpret_cast<std::shared_ptr<T> *>(ptr_entry->second);
return true;
}
}
// find the actual value and deserialize it, storing the shared_ptr
const auto entry = doc.FindMember(StringRef(name.data(), name.length()));
if (entry == doc.MemberEnd()) {
return false;
}
if (type_ptrs_entry == objects.end()) {
objects[name] = TypedPointers();
}
auto &ptrs = objects[name];
value = std::make_shared<T>();
ptrs[type_id] = &value; // add to map *before* deserializing
if (!serializer<T>::deserialize(entry->value, *value, doc, objects)) {
ptrs.erase(type_id); // remove on failure
return false;
}
return true;
}
};
///////////////////////////////////////////////////////////////////////////////
// std::complex
template <typename T>
struct serializer<std::complex<T>> {
RAPIDJSON_PERSIST_SERIALIZE(std::complex<T>)
{
using Char = typename Encoding::Ch;
auto json = RAPIDJSON_PERSIST_VALUE(Type::kObjectType);
json.AddMember(StringRef(literals<Char>::real.data(), literals<Char>::real.length()),
serializer<T>::serialize(value.real(), doc), doc.GetAllocator());
json.AddMember(StringRef(literals<Char>::imag.data(), literals<Char>::imag.length()),
serializer<T>::serialize(value.imag(), doc), doc.GetAllocator());
return json;
}
RAPIDJSON_PERSIST_DESERIALIZE(std::complex<T>)
{
using Char = typename Encoding::Ch;
if (!json.IsObject()) {
return false;
}
auto success = true;
auto real = T();
auto real_member = json.FindMember(StringRef(literals<Char>::real.data(), literals<Char>::real.length()));
if (real_member != json.MemberEnd() && serializer<T>::deserialize(real_member->value, real, doc, objects)) {
value.real(real);
} else {
success = false;
}
auto imag = T();
auto imag_member = json.FindMember(StringRef(literals<Char>::imag.data(), literals<Char>::imag.length()));
if (imag_member != json.MemberEnd() && serializer<T>::deserialize(imag_member->value, imag, doc, objects)) {
value.imag(imag);
} else {
success = false;
}
return success;
}
};
///////////////////////////////////////////////////////////////////////////////
// std::unique_ptr (no custom deleters), std::optional
template <template <typename> typename Holder, typename T>
struct serializer<Holder<T>, std::enable_if_t<std::is_same_v<std::unique_ptr<T>, Holder<T>> ||
std::is_same_v<std::optional<T>, Holder<T>>>> {
RAPIDJSON_PERSIST_SERIALIZE(Holder<T>)
{
return value ? serializer<T>::serialize(*value, doc) : RAPIDJSON_PERSIST_VALUE();
}
RAPIDJSON_PERSIST_DESERIALIZE(Holder<T>)
{
if (json.IsNull()) {
value.reset();
return true;
}
if constexpr (std::is_same_v<std::unique_ptr<T>, Holder<T>>) {
value = std::make_unique<T>();
} else {
value = std::make_optional<T>();
}
return serializer<T>::deserialize(json, *value, doc, objects);
}
};
///////////////////////////////////////////////////////////////////////////////
// enums
template <typename Enum, typename Char>
struct enum_format;
template <typename Char>
static std::enable_if_t<std::is_same_v<Char, char>, bool> is_space(Char ch)
{
return std::isspace(ch) != 0;
}
template <typename Char>
static std::enable_if_t<std::is_same_v<Char, wchar_t>, bool> is_space(Char ch)
{
return std::iswspace(ch) != 0;
}
template <typename Enum>
struct formatter<Enum, std::enable_if_t<std::is_enum_v<Enum>>> {
using underlying_type = std::underlying_type_t<Enum>;
template <typename Encoding>
static std::basic_string<typename Encoding::Ch> format(const Enum &value)
{
using Char = typename Encoding::Ch;
using T = enum_format<Enum, Char>;
auto str = std::basic_string<Char>();
auto first = true;
auto raw_value = static_cast<underlying_type>(value);
if constexpr (std::is_constructible_v<T>) {
for (const auto &mask : T::masks) {
const auto &mask_raw = mask.second;
for (const auto &[value_name, value_raw] : T::values) {
if ((value_raw & mask_raw) && !(value_raw & ~mask_raw) && ((raw_value & mask_raw) == value_raw)) {
if (first) {
first = false;
} else {
str.append(literals<Char>::sep);
}
str.append(value_name);
raw_value &= ~mask_raw;
}
}
}
for (const auto &[flag_name, flag_raw] : T::flags) {
if ((flag_raw & raw_value) == flag_raw) {
if (first) {
first = false;
} else {
str.append(literals<Char>::sep);
}
str.append(flag_name);
raw_value &= ~flag_raw;
}
}
for (const auto &[value_name, value_raw] : T::values) {
if (value_raw == raw_value) {
if (first) {
first = false;
} else {
str.append(literals<Char>::sep);
}
str.append(value_name);
return str;
}
}
}
if (!first) {
if (!raw_value) {
return str;
}
str.append(literals<Char>::sep);
}
str.append(formatter<underlying_type>::template format<Encoding>(raw_value));
return str;
}
template <typename Encoding>
static Enum parse(const std::basic_string<typename Encoding::Ch> &str)
{
using Char = typename Encoding::Ch;
using T = enum_format<Enum, Char>;
constexpr const auto npos = std::basic_string<Char>::npos;
auto raw_value = underlying_type();
auto is_empty = true;
for (auto offset = std::size_t(0), sep_pos = std::size_t(0); sep_pos != npos; offset = sep_pos + 1) {
while (offset < str.length() && is_space(str[offset])) {
offset++;
}
sep_pos = str.find(literals<Char>::sep_char, offset);
auto offset_end = sep_pos == npos ? str.length() : sep_pos;
while (offset_end > offset && is_space(str[offset_end - 1])) {
offset_end--;
}
if (offset_end <= offset) {
continue;
}
is_empty = false;
const auto part = std::basic_string<Char>(str, offset, offset_end - offset);
if constexpr (std::is_constructible_v<T>) {
const auto flag = T::flags.find(part);
if (flag != T::flags.end()) {
raw_value |= flag->second;
continue;
}
const auto enum_value = T::values.find(part);
if (enum_value != T::values.end()) {
raw_value |= enum_value->second;
continue;
}
}
raw_value |= formatter<underlying_type>::template parse<Encoding>(part);
}
if (is_empty) {
throw std::logic_error("empty enum value");
};
return static_cast<Enum>(raw_value);
}
};
// clang-format off
#define RAPIDJSON_PERSIST_ENUM_BEGIN_(char_type, enum_type) \
namespace RAPIDJSON_NAMESPACE::persist { \
template <> \
struct enum_format<enum_type, char_type> { \
using type = enum_type; \
using underlying_type = std::underlying_type_t<enum_type>; \
private: \
static inline const auto _maps = ([](){ \
std::unordered_map<std::basic_string<char_type>, underlying_type> _flags, _masks, _values;
#define RAPIDJSON_PERSIST_ENUM_VALUE_(prefix, char_type, set, name) \
set[std::basic_string<char_type>(prefix## #name)] = static_cast<underlying_type>(type::name)
#define RAPIDJSON_PERSIST_ENUM_END() \
return std::make_tuple(_flags, _masks, _values); \
})(); \
public: \
static inline const auto &flags = std::get<0>(_maps); \
static inline const auto &masks = std::get<1>(_maps); \
static inline const auto &values = std::get<2>(_maps); \
}; \
}
// clang-format on
#define RAPIDJSON_PERSIST_ENUM_BEGIN_A(type) RAPIDJSON_PERSIST_ENUM_BEGIN_(char, type)
#define RAPIDJSON_PERSIST_ENUM_BEGIN_W(type) RAPIDJSON_PERSIST_ENUM_BEGIN_(wchar_t, type)
#define RAPIDJSON_PERSIST_ENUM_FLAG_A(name) RAPIDJSON_PERSIST_ENUM_VALUE_(, char, _flags, name)
#define RAPIDJSON_PERSIST_ENUM_FLAG_W(name) RAPIDJSON_PERSIST_ENUM_VALUE_(L, wchar_t, _flags, name)
#define RAPIDJSON_PERSIST_ENUM_MASK_A(name) RAPIDJSON_PERSIST_ENUM_VALUE_(, char, _masks, name)
#define RAPIDJSON_PERSIST_ENUM_MASK_W(name) RAPIDJSON_PERSIST_ENUM_VALUE_(L, wchar_t, _masks, name)
#define RAPIDJSON_PERSIST_ENUM_VALUE_A(name) RAPIDJSON_PERSIST_ENUM_VALUE_(, char, _values, name)
#define RAPIDJSON_PERSIST_ENUM_VALUE_W(name) RAPIDJSON_PERSIST_ENUM_VALUE_(L, wchar_t, _values, name)
#if RAPIDJSON_PERSIST_UNICODE
#define RAPIDJSON_PERSIST_ENUM_BEGIN RAPIDJSON_PERSIST_ENUM_BEGIN_W
#define RAPIDJSON_PERSIST_ENUM_FLAG RAPIDJSON_PERSIST_ENUM_FLAG_W
#define RAPIDJSON_PERSIST_ENUM_MASK RAPIDJSON_PERSIST_ENUM_MASK_W
#define RAPIDJSON_PERSIST_ENUM_VALUE RAPIDJSON_PERSIST_ENUM_VALUE_W
#else
#define RAPIDJSON_PERSIST_ENUM_BEGIN RAPIDJSON_PERSIST_ENUM_BEGIN_A
#define RAPIDJSON_PERSIST_ENUM_FLAG RAPIDJSON_PERSIST_ENUM_FLAG_A
#define RAPIDJSON_PERSIST_ENUM_MASK RAPIDJSON_PERSIST_ENUM_MASK_A
#define RAPIDJSON_PERSIST_ENUM_VALUE RAPIDJSON_PERSIST_ENUM_VALUE_A
#endif
///////////////////////////////////////////////////////////////////////////////
// std::unordered_map, std::map
template <typename Key, typename T, typename Map, RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
static RAPIDJSON_PERSIST_SERIALIZE_INTERNAL(serialize_map, Map)
{
using Char = typename Encoding::Ch;
if constexpr (std::is_constructible_v<formatter<Key>>) {
auto json = RAPIDJSON_PERSIST_VALUE(Type::kObjectType);
for (const auto &[key, sub_value] : value) {
const auto name = formatter<Key>::template format<Encoding>(key);
json.AddMember(RAPIDJSON_PERSIST_VALUE(name.data(), name.length(), doc.GetAllocator()),
serializer<T>::serialize(sub_value, doc), doc.GetAllocator());
}
return json;
} else {
auto json = RAPIDJSON_PERSIST_VALUE(Type::kArrayType);
for (const auto &[key, sub_value] : value) {
auto entry = RAPIDJSON_PERSIST_VALUE(Type::kObjectType);
entry.AddMember(StringRef(literals<Char>::key.data(), literals<Char>::key.length()),
serializer<Key>::serialize(key, doc), doc.GetAllocator());
entry.AddMember(StringRef(literals<Char>::value.data(), literals<Char>::value.length()),
serializer<T>::serialize(sub_value, doc), doc.GetAllocator());
json.PushBack(entry, doc.GetAllocator());
}
return json;
}
}
template <typename Key, typename T, typename Map, RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
static RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(deserialize_map, Map)
{
using Char = typename Encoding::Ch;
if constexpr (std::is_constructible_v<formatter<Key>>) {
if (!json.IsObject()) {
return false;
}
auto success = true;
value.clear();
for (const auto &entry : json.GetObject()) {
const auto name = std::basic_string<Char>(entry.name.GetString(), entry.name.GetStringLength());
auto index = Key();
try {
index = formatter<Key>::template parse<Encoding>(name);
} catch (const std::logic_error &) {
success = false;
continue;
}
const auto inserted = value.emplace(std::piecewise_construct, std::make_tuple(index), std::make_tuple());
success &= serializer<T>::deserialize(entry.value, inserted.first->second, doc, objects);
}
return success;
} else {
if (!json.IsArray()) {
return false;
}
auto success = true;
value.clear();
for (const auto &element : json.GetArray()) {
if (!element.IsObject()) {
success = false;
continue;
}
const auto key_entry =
element.FindMember(StringRef(literals<Char>::key.data(), literals<Char>::key.length()));
if (key_entry == element.MemberEnd()) {
success = false;
continue;
}
auto index = Key();
if (!serializer<Key>::deserialize(key_entry->value, index, doc, objects)) {
success = false;
continue;
}
const auto inserted = value.emplace(std::piecewise_construct, std::make_tuple(index), std::make_tuple());
const auto value_entry =
element.FindMember(StringRef(literals<Char>::value.data(), literals<Char>::value.length()));
success &= value_entry != element.MemberEnd() &&
serializer<T>::deserialize(value_entry->value, inserted.first->second, doc, objects);
}
return success;
}
}
template <typename Key, typename T, typename Hash, typename EqualTo, typename Allocator>
struct serializer<std::unordered_map<Key, T, Hash, EqualTo, Allocator>> {
RAPIDJSON_PERSIST_SERIALIZE(std::unordered_map<Key, T, Hash, EqualTo, Allocator>)
{
return serialize_map<Key, T>(value, doc);
}
RAPIDJSON_PERSIST_DESERIALIZE(std::unordered_map<Key, T, Hash, EqualTo, Allocator>)
{
return deserialize_map<Key, T>(json, value, doc, objects);
}
};
template <typename Key, typename T, typename Less, typename Allocator>
struct serializer<std::map<Key, T, Less, Allocator>> {
RAPIDJSON_PERSIST_SERIALIZE(std::map<Key, T, Less, Allocator>) { return serialize_map<Key, T>(value, doc); }
RAPIDJSON_PERSIST_DESERIALIZE(std::map<Key, T, Less, Allocator>)
{
return deserialize_map<Key, T>(json, value, doc, objects);
}
};
///////////////////////////////////////////////////////////////////////////////
// std::unordered_set, std::set
template <typename T, typename Set, RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
static RAPIDJSON_PERSIST_SERIALIZE_INTERNAL(serialize_set, Set)
{
auto json = RAPIDJSON_PERSIST_VALUE(Type::kArrayType);
for (const auto &element : value) {
json.PushBack(serializer<T>::serialize(element, doc), doc.GetAllocator());
}
return json;
}
template <typename T, typename Set, RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
static RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(deserialize_set, Set)
{
if (!json.IsArray()) {
return false;
}
auto success = true;
value.clear();
for (const auto &element : json.GetArray()) {
auto sub_value = T();
if (serializer<T>::deserialize(element, sub_value, doc, objects)) {
value.insert(sub_value);
} else {
success = false;
}
}
return success;
}
template <typename T, typename Hash, typename EqualTo, typename Allocator>
struct serializer<std::unordered_set<T, Hash, EqualTo, Allocator>> {
RAPIDJSON_PERSIST_SERIALIZE(std::unordered_set<T, Hash, EqualTo, Allocator>)
{
return serialize_set<T>(value, doc);
}
RAPIDJSON_PERSIST_DESERIALIZE(std::unordered_set<T, Hash, EqualTo, Allocator>)
{
return deserialize_set<T>(json, value, doc, objects);
}
};
template <typename T, typename Less, typename Allocator>
struct serializer<std::set<T, Less, Allocator>> {
RAPIDJSON_PERSIST_SERIALIZE(std::set<T, Less, Allocator>) { return serialize_set<T>(value, doc); }
RAPIDJSON_PERSIST_DESERIALIZE(std::set<T, Less, Allocator>)
{
return deserialize_set<T>(json, value, doc, objects);
}
};
///////////////////////////////////////////////////////////////////////////////
// T[N], std::array
template <template <typename, std::size_t> typename Array, typename T, std::size_t N>
struct serializer<
Array<T, N>, std::enable_if_t<std::is_same_v<Array<T, N>, std::array<T, N>> || std::is_same_v<Array<T, N>, T[N]>>> {
RAPIDJSON_PERSIST_SERIALIZE(Array<T, N>)
{
auto json = RAPIDJSON_PERSIST_VALUE(Type::kArrayType);
for (auto i = std::size_t(0); i < N; i++) {
json.PushBack(serializer<T>::serialize(value[i], doc), doc.GetAllocator());
}
return json;
}
RAPIDJSON_PERSIST_DESERIALIZE(Array<T, N>)
{
if (!json.IsArray()) {
return false;
}
auto success = true;
for (auto i = std::size_t(0), limit = std::min<std::size_t>(N, json.Size()); i < limit; i++) {
success &= serializer<T>::deserialize(json[i], value[i], doc, objects);
}
return success && json.Size() == N;
}
};
///////////////////////////////////////////////////////////////////////////////
// std::tuple
template <typename... Types>
struct serializer<std::tuple<Types...>> {
private:
template <std::size_t... Indices, typename Tuple, RAPIDJSON_PERSIST_TEMPLATE_INTERNAL>
static RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(deserialize_internal, std::index_sequence<Indices...>, Tuple)
{
return (serializer<std::remove_reference_t<decltype(std::get<Indices>(value))>>::deserialize(
json[Indices], std::get<Indices>(value), doc, objects) &
...);
}
public:
RAPIDJSON_PERSIST_SERIALIZE(std::tuple<Types...>)
{
auto json = RAPIDJSON_PERSIST_VALUE(Type::kArrayType);
std::apply(
[&json, &doc](const auto &... sub_value) {
(...,
json.PushBack(serializer<std::remove_const_t<std::remove_reference_t<decltype(sub_value)>>>::serialize(
sub_value, doc),
doc.GetAllocator()));
},
value);
return json;
}
RAPIDJSON_PERSIST_DESERIALIZE(std::tuple<Types...>)
{
if (!json.IsArray() || json.Size() != sizeof...(Types)) {
return false;
}
return deserialize_internal(json, std::index_sequence_for<Types...>{}, value, doc, objects);
}
};
///////////////////////////////////////////////////////////////////////////////
// std::vector, std::list, std::deque
template <typename T, template <typename, typename> typename U, typename Allocator>
struct serializer<U<T, Allocator>, std::enable_if_t<std::is_same_v<U<T, Allocator>, std::vector<T, Allocator>> ||
std::is_same_v<U<T, Allocator>, std::list<T, Allocator>> ||
std::is_same_v<U<T, Allocator>, std::deque<T, Allocator>>>> {
RAPIDJSON_PERSIST_SERIALIZE(U<T, Allocator>)
{
auto json = RAPIDJSON_PERSIST_VALUE(Type::kArrayType);
for (const auto &element : value) {
json.PushBack(serializer<T>::serialize(element, doc), doc.GetAllocator());
}
return json;
}
RAPIDJSON_PERSIST_DESERIALIZE(U<T, Allocator>)
{
if (!json.IsArray()) {
return false;
}
auto success = true;
value.clear();
for (const auto &element : json.GetArray()) {
auto &sub_value = value.emplace_back();
success &= serializer<T>::deserialize(element, sub_value, doc, objects);
}
return success;
}
};
///////////////////////////////////////////////////////////////////////////////
// serializable
template <typename T,
typename Encoding =
#if RAPIDJSON_PERSIST_UNICODE
UTF16<>
#else
UTF8<>
#endif
,
typename HeapAllocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
class serializable {
private:
using Char = typename Encoding::Ch;
struct member_t {
private:
using serialize_t = std::function<RAPIDJSON_PERSIST_SERIALIZE_INTERNAL(, T)>;
using deserialize_t = std::function<RAPIDJSON_PERSIST_DESERIALIZE_INTERNAL(, T)>;
public:
member_t(const Char *name, bool checked, serialize_t serialize, deserialize_t deserialize)
: name(name), checked(checked), serialize(serialize), deserialize(deserialize)
{
}
const Char *const name;
const bool checked;
const serialize_t serialize;
const deserialize_t deserialize;
};
static inline auto members = std::list<member_t>();
RAPIDJSON_PERSIST_VALUE serialize(RAPIDJSON_PERSIST_DOCUMENT &doc) const
{
auto json = RAPIDJSON_PERSIST_VALUE(Type::kObjectType);
for (const auto &member : serializable::members) {
json.AddMember(StringRef(member.name), member.serialize(*static_cast<const T *>(this), doc),
doc.GetAllocator());
}
return json;
}
bool deserialize(const RAPIDJSON_PERSIST_VALUE &json, const RAPIDJSON_PERSIST_DOCUMENT &doc,
NamedObjects<Encoding> &objects)
{
if (!json.IsObject()) {
return false;
}
auto success = true;
for (const auto &member : serializable::members) {
const auto member_entry = json.FindMember(member.name);
success &= (member_entry != json.MemberEnd() &&
member.deserialize(member_entry->value, *static_cast<T *>(this), doc, objects)) ||
!member.checked;
}
return success;
}
friend class serializer<T>;
public:
template <typename U>
static bool register_member(std::function<U &(T &)> member, const Char *name, bool checked)
{
members.emplace_back(name, checked,
[member](const T &instance, RAPIDJSON_PERSIST_DOCUMENT &doc) -> RAPIDJSON_PERSIST_VALUE {
return serializer<U>::serialize(member(const_cast<T &>(instance)), doc);
},
[member](const RAPIDJSON_PERSIST_VALUE &json, T &instance,
const RAPIDJSON_PERSIST_DOCUMENT &doc, NamedObjects<Encoding> &objects) -> bool {
return serializer<U>::deserialize(json, member(const_cast<T &>(instance)), doc,
objects);
});
return true;
}
RAPIDJSON_PERSIST_DOCUMENT serialize() const
{
auto doc = RAPIDJSON_PERSIST_DOCUMENT(Type::kObjectType);
auto main = serialize(doc);
if (doc.MemberCount() == 0) {
doc.SetObject() = main.Move();
} else {
doc.AddMember(StringRef(literals<Char>::main.data(), literals<Char>::main.length()), main,
doc.GetAllocator());
}
return doc;
}
static std::optional<T> deserialize(const RAPIDJSON_PERSIST_DOCUMENT &doc)
{
if (!doc.IsObject()) {
return std::nullopt;
}
auto value = std::make_optional<T>();
auto objects = NamedObjects<Encoding>();
const auto main_entry = doc.FindMember(StringRef(literals<Char>::main.data(), literals<Char>::main.length()));
if (!value->deserialize(main_entry == doc.MemberEnd() ? doc : main_entry->value, doc, objects)) {
return std::nullopt;
}
return value;
}
};
///////////////////////////////////////////////////////////////////////////////
// main macros
#define RAPIDJSON_PERSIST_INTERNAL(prefix, name, checked, ...) \
static inline const auto _serializable_##name = serializable::register_member<__VA_ARGS__>( \
[](auto &instance) -> __VA_ARGS__ & { return instance.name; }, prefix## #name, checked); \
__VA_ARGS__ name
#define RAPIDJSON_PERSIST_(prefix, name, ...) RAPIDJSON_PERSIST_INTERNAL(prefix, name, false, __VA_ARGS__)
#define RAPIDJSON_PERSIST_A(name, ...) RAPIDJSON_PERSIST_(, name, __VA_ARGS__)
#define RAPIDJSON_PERSIST_W(name, ...) RAPIDJSON_PERSIST_(L, name, __VA_ARGS__)
#if RAPIDJSON_PERSIST_UNICODE
#define RAPIDJSON_PERSIST RAPIDJSON_PERSIST_W
#else
#define RAPIDJSON_PERSIST RAPIDJSON_PERSIST_A
#endif
#define RAPIDJSON_PERSIST_CHECKED_(prefix, name, ...) RAPIDJSON_PERSIST_INTERNAL(prefix, name, true, __VA_ARGS__)
#define RAPIDJSON_PERSIST_CHECKED_A(name, ...) RAPIDJSON_PERSIST_CHECKED_(, name, __VA_ARGS__)
#define RAPIDJSON_PERSIST_CHECKED_W(name, ...) RAPIDJSON_PERSIST_CHECKED_(L, name, __VA_ARGS__)
#if RAPIDJSON_PERSIST_UNICODE
#define RAPIDJSON_PERSIST_CHECKED RAPIDJSON_PERSIST_CHECKED_W
#else
#define RAPIDJSON_PERSIST_CHECKED RAPIDJSON_PERSIST_CHECKED_A
#endif
} // namespace persist
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_PERSIST_H_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment