1136 lines
29 KiB
C++
1136 lines
29 KiB
C++
|
#pragma once
|
||
|
|
||
|
#include <type_traits>
|
||
|
#include <iterator>
|
||
|
#include <iostream>
|
||
|
#include <fstream>
|
||
|
#include <stack>
|
||
|
#include <queue>
|
||
|
#include <vector>
|
||
|
#include <algorithm>
|
||
|
#include <memory>
|
||
|
#include <functional>
|
||
|
#include <map>
|
||
|
#include <set>
|
||
|
#include <unordered_map>
|
||
|
#include <unordered_set>
|
||
|
#include <sstream>
|
||
|
#include <list>
|
||
|
#include <forward_list>
|
||
|
#include <numeric>
|
||
|
#include <iomanip>
|
||
|
#include <cassert>
|
||
|
#include <cmath>
|
||
|
#include <array>
|
||
|
#include <string>
|
||
|
#include <variant>
|
||
|
#include <optional>
|
||
|
|
||
|
namespace tf {
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// Supported C++ STL type
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
// std::basic_string
|
||
|
template <typename T>
|
||
|
struct is_std_basic_string : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_basic_string <std::basic_string<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_basic_string_v = is_std_basic_string<T>::value;
|
||
|
|
||
|
// std::array
|
||
|
template <typename T>
|
||
|
struct is_std_array : std::false_type {};
|
||
|
|
||
|
template <typename T, size_t N>
|
||
|
struct is_std_array <std::array<T, N>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_array_v = is_std_array<T>::value;
|
||
|
|
||
|
// std::vector
|
||
|
template <typename T>
|
||
|
struct is_std_vector : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_vector <std::vector<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_vector_v = is_std_vector<T>::value;
|
||
|
|
||
|
// std::deque
|
||
|
template <typename T>
|
||
|
struct is_std_deque : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_deque <std::deque<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_deque_v = is_std_deque<T>::value;
|
||
|
|
||
|
// std::list
|
||
|
template <typename T>
|
||
|
struct is_std_list : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_list <std::list<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_list_v = is_std_list<T>::value;
|
||
|
|
||
|
// std::forward_list
|
||
|
template <typename T>
|
||
|
struct is_std_forward_list : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_forward_list <std::forward_list<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_forward_list_v = is_std_forward_list<T>::value;
|
||
|
|
||
|
// std::map
|
||
|
template <typename T>
|
||
|
struct is_std_map : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_map <std::map<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_map_v = is_std_map<T>::value;
|
||
|
|
||
|
// std::unordered_map
|
||
|
template <typename T>
|
||
|
struct is_std_unordered_map : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_unordered_map <std::unordered_map<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_unordered_map_v = is_std_unordered_map<T>::value;
|
||
|
|
||
|
// std::set
|
||
|
template <typename T>
|
||
|
struct is_std_set : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_set <std::set<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_set_v = is_std_set<T>::value;
|
||
|
|
||
|
// std::unordered_set
|
||
|
template <typename T>
|
||
|
struct is_std_unordered_set : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_unordered_set <std::unordered_set<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_unordered_set_v = is_std_unordered_set<T>::value;
|
||
|
|
||
|
// std::variant
|
||
|
template <typename T>
|
||
|
struct is_std_variant : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_variant <std::variant<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_variant_v = is_std_variant<T>::value;
|
||
|
|
||
|
// std::optional
|
||
|
template <typename T>
|
||
|
struct is_std_optional : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_optional <std::optional<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_optional_v = is_std_optional<T>::value;
|
||
|
|
||
|
// std::unique_ptr
|
||
|
template <typename T>
|
||
|
struct is_std_unique_ptr : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_unique_ptr <std::unique_ptr<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_unique_ptr_v = is_std_unique_ptr<T>::value;
|
||
|
|
||
|
// std::shared_ptr
|
||
|
template <typename T>
|
||
|
struct is_std_shared_ptr : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_shared_ptr <std::shared_ptr<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_shared_ptr_v = is_std_shared_ptr<T>::value;
|
||
|
|
||
|
// std::duration
|
||
|
template <typename T> struct is_std_duration : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_duration<std::chrono::duration<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_duration_v = is_std_duration<T>::value;
|
||
|
|
||
|
// std::time_point
|
||
|
template <typename T>
|
||
|
struct is_std_time_point : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_time_point<std::chrono::time_point<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_time_point_v = is_std_time_point<T>::value;
|
||
|
|
||
|
// std::tuple
|
||
|
template <typename T>
|
||
|
struct is_std_tuple : std::false_type {};
|
||
|
|
||
|
template <typename... ArgsT>
|
||
|
struct is_std_tuple<std::tuple<ArgsT...>> : std::true_type {};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr bool is_std_tuple_v = is_std_tuple<T>::value;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Type extraction.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
// ExtractType: forward declaration
|
||
|
template <size_t, typename>
|
||
|
struct ExtractType;
|
||
|
|
||
|
// ExtractType_t: alias interface
|
||
|
template <size_t idx, typename C>
|
||
|
using ExtractType_t = typename ExtractType<idx, C>::type;
|
||
|
|
||
|
// ExtractType: base
|
||
|
template <template <typename...> typename C, typename T, typename... RestT>
|
||
|
struct ExtractType <0, C<T, RestT...>> {
|
||
|
using type = T;
|
||
|
};
|
||
|
|
||
|
// ExtractType: base
|
||
|
template <typename T>
|
||
|
struct ExtractType <0, T> {
|
||
|
using type = T;
|
||
|
};
|
||
|
|
||
|
// ExtractType: recursive definition.
|
||
|
template <size_t idx, template <typename...> typename C, typename T, typename... RestT>
|
||
|
struct ExtractType <idx, C<T, RestT...>> : ExtractType<idx-1, C<RestT...>> {
|
||
|
};
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// Size Wrapper
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
// Struct: SizeTag
|
||
|
// Class that wraps a given size item which can be customized.
|
||
|
template <typename T>
|
||
|
class SizeTag {
|
||
|
|
||
|
public:
|
||
|
|
||
|
using type = std::conditional_t<std::is_lvalue_reference_v<T>, T, std::decay_t<T>>;
|
||
|
|
||
|
SizeTag(T&& item) : _item(std::forward<T>(item)) {}
|
||
|
|
||
|
SizeTag& operator = (const SizeTag&) = delete;
|
||
|
|
||
|
inline const T& get() const {return _item;}
|
||
|
|
||
|
template <typename ArchiverT>
|
||
|
auto save(ArchiverT & ar) const { return ar(_item); }
|
||
|
|
||
|
template <typename ArchiverT>
|
||
|
auto load(ArchiverT & ar) { return ar(_item); }
|
||
|
|
||
|
private:
|
||
|
|
||
|
type _item;
|
||
|
};
|
||
|
|
||
|
// Function: make_size_tag
|
||
|
template <typename T>
|
||
|
SizeTag<T> make_size_tag(T&& t) {
|
||
|
return { std::forward<T>(t) };
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// Size Wrapper
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
// Class: MapItem
|
||
|
template <typename KeyT, typename ValueT>
|
||
|
class MapItem {
|
||
|
|
||
|
public:
|
||
|
|
||
|
using KeyType = std::conditional_t <std::is_lvalue_reference_v<KeyT>, KeyT, std::decay_t<KeyT>>;
|
||
|
using ValueType = std::conditional_t <std::is_lvalue_reference_v<ValueT>, ValueT, std::decay_t<ValueT>>;
|
||
|
|
||
|
MapItem(KeyT&& k, ValueT&& v) : _key(std::forward<KeyT>(k)), _value(std::forward<ValueT>(v)) {}
|
||
|
MapItem& operator = (const MapItem&) = delete;
|
||
|
|
||
|
inline const KeyT& key() const { return _key; }
|
||
|
inline const ValueT& value() const { return _value; }
|
||
|
|
||
|
template <typename ArchiverT>
|
||
|
auto save(ArchiverT & ar) const { return ar(_key, _value); }
|
||
|
|
||
|
template <typename ArchiverT>
|
||
|
auto load(ArchiverT & ar) { return ar(_key, _value); }
|
||
|
|
||
|
private:
|
||
|
|
||
|
KeyType _key;
|
||
|
ValueType _value;
|
||
|
};
|
||
|
|
||
|
// Function: make_kv_pair
|
||
|
template <typename KeyT, typename ValueT>
|
||
|
MapItem<KeyT, ValueT> make_kv_pair(KeyT&& k, ValueT&& v) {
|
||
|
return { std::forward<KeyT>(k), std::forward<ValueT>(v) };
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// Serializer Definition
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr auto is_default_serializable_v = (
|
||
|
std::is_arithmetic_v<T> ||
|
||
|
std::is_enum_v<T> ||
|
||
|
is_std_basic_string_v<T> ||
|
||
|
is_std_vector_v<T> ||
|
||
|
is_std_deque_v<T> ||
|
||
|
is_std_list_v<T> ||
|
||
|
is_std_forward_list_v<T> ||
|
||
|
is_std_map_v<T> ||
|
||
|
is_std_unordered_map_v<T> ||
|
||
|
is_std_set_v<T> ||
|
||
|
is_std_unordered_set_v<T> ||
|
||
|
is_std_duration_v<T> ||
|
||
|
is_std_time_point_v<T> ||
|
||
|
is_std_variant_v<T> ||
|
||
|
is_std_optional_v<T> ||
|
||
|
is_std_tuple_v<T> ||
|
||
|
is_std_array_v<T>
|
||
|
);
|
||
|
|
||
|
|
||
|
// Class: Serializer
|
||
|
template <typename Stream, typename SizeType = std::streamsize>
|
||
|
class Serializer {
|
||
|
|
||
|
public:
|
||
|
|
||
|
Serializer(Stream& stream);
|
||
|
|
||
|
template <typename... T>
|
||
|
SizeType operator()(T&&... items);
|
||
|
|
||
|
private:
|
||
|
|
||
|
Stream& _stream;
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<!is_default_serializable_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_arithmetic_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_basic_string_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_vector_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<
|
||
|
is_std_deque_v<std::decay_t<T>> ||
|
||
|
is_std_list_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<
|
||
|
is_std_forward_list_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<
|
||
|
is_std_map_v<std::decay_t<T>> ||
|
||
|
is_std_unordered_map_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<
|
||
|
is_std_set_v<std::decay_t<T>> ||
|
||
|
is_std_unordered_set_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_enum_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_duration_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_time_point_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_optional_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_variant_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_tuple_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_array_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _save(T&&);
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
// Constructor
|
||
|
template <typename Stream, typename SizeType>
|
||
|
Serializer<Stream, SizeType>::Serializer(Stream& stream) : _stream(stream) {
|
||
|
}
|
||
|
|
||
|
// Operator ()
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename... T>
|
||
|
SizeType Serializer<Stream, SizeType>::operator() (T&&... items) {
|
||
|
return (_save(std::forward<T>(items)) + ...);
|
||
|
}
|
||
|
|
||
|
// arithmetic data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_arithmetic_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
_stream.write(reinterpret_cast<const char*>(std::addressof(t)), sizeof(t));
|
||
|
return sizeof(t);
|
||
|
}
|
||
|
|
||
|
// std::basic_string
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_basic_string_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
auto sz = _save(make_size_tag(t.size()));
|
||
|
_stream.write(
|
||
|
reinterpret_cast<const char*>(t.data()),
|
||
|
t.size()*sizeof(typename U::value_type)
|
||
|
);
|
||
|
return sz + t.size()*sizeof(typename U::value_type);
|
||
|
}
|
||
|
|
||
|
// std::vector
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_vector_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
auto sz = _save(make_size_tag(t.size()));
|
||
|
|
||
|
if constexpr (std::is_arithmetic_v<typename U::value_type>) {
|
||
|
_stream.write(
|
||
|
reinterpret_cast<const char*>(t.data()),
|
||
|
t.size() * sizeof(typename U::value_type)
|
||
|
);
|
||
|
sz += t.size() * sizeof(typename U::value_type);
|
||
|
} else {
|
||
|
for(auto&& item : t) {
|
||
|
sz += _save(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::list and std::deque
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_deque_v<std::decay_t<T>> ||
|
||
|
is_std_list_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
auto sz = _save(make_size_tag(t.size()));
|
||
|
for(auto&& item : t) {
|
||
|
sz += _save(item);
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::forward_list
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_forward_list_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
auto sz = _save(make_size_tag(std::distance(t.begin(), t.end())));
|
||
|
for(auto&& item : t) {
|
||
|
sz += _save(item);
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::map and std::unordered_map
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T, std::enable_if_t<
|
||
|
is_std_map_v<std::decay_t<T>> ||
|
||
|
is_std_unordered_map_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>*>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
auto sz = _save(make_size_tag(t.size()));
|
||
|
for(auto&& [k, v] : t) {
|
||
|
sz += _save(make_kv_pair(k, v));
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::set and std::unordered_set
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T, std::enable_if_t<
|
||
|
is_std_set_v<std::decay_t<T>> ||
|
||
|
is_std_unordered_set_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>*>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
auto sz = _save(make_size_tag(t.size()));
|
||
|
for(auto&& item : t) {
|
||
|
sz += _save(item);
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// enum data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_enum_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
return _save(static_cast<std::underlying_type_t<U>>(t));
|
||
|
}
|
||
|
|
||
|
// duration data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_duration_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
return _save(t.count());
|
||
|
}
|
||
|
|
||
|
// time point data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_time_point_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
return _save(t.time_since_epoch());
|
||
|
}
|
||
|
|
||
|
// optional data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_optional_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
if(bool flag = t.has_value(); flag) {
|
||
|
return _save(flag) + _save(*t);
|
||
|
}
|
||
|
else {
|
||
|
return _save(flag);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// variant type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_variant_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
return _save(t.index()) +
|
||
|
std::visit([&] (auto&& arg){ return _save(arg);}, t);
|
||
|
}
|
||
|
|
||
|
// tuple type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_tuple_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
return std::apply(
|
||
|
[&] (auto&&... args) {
|
||
|
return (_save(std::forward<decltype(args)>(args)) + ... + 0);
|
||
|
},
|
||
|
std::forward<T>(t)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// array
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_array_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
static_assert(std::tuple_size<U>::value > 0, "Array size can't be zero");
|
||
|
|
||
|
SizeType sz;
|
||
|
|
||
|
if constexpr(std::is_arithmetic_v<typename U::value_type>) {
|
||
|
_stream.write(reinterpret_cast<const char*>(t.data()), sizeof(t));
|
||
|
sz = sizeof(t);
|
||
|
}
|
||
|
else {
|
||
|
sz = 0;
|
||
|
for(auto&& item : t) {
|
||
|
sz += _save(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// custom save method
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<!is_default_serializable_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Serializer<Stream, SizeType>::_save(T&& t) {
|
||
|
return t.save(*this);
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// DeSerializer Definition
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr auto is_default_deserializable_v =
|
||
|
std::is_arithmetic_v<T> ||
|
||
|
std::is_enum_v<T> ||
|
||
|
is_std_basic_string_v<T> ||
|
||
|
is_std_vector_v<T> ||
|
||
|
is_std_deque_v<T> ||
|
||
|
is_std_list_v<T> ||
|
||
|
is_std_forward_list_v<T> ||
|
||
|
is_std_map_v<T> ||
|
||
|
is_std_unordered_map_v<T> ||
|
||
|
is_std_set_v<T> ||
|
||
|
is_std_unordered_set_v<T> ||
|
||
|
is_std_duration_v<T> ||
|
||
|
is_std_time_point_v<T> ||
|
||
|
is_std_variant_v<T> ||
|
||
|
is_std_optional_v<T> ||
|
||
|
is_std_tuple_v<T> ||
|
||
|
is_std_array_v<T>;
|
||
|
|
||
|
// Class: Deserializer
|
||
|
template <typename Stream, typename SizeType = std::streamsize>
|
||
|
class Deserializer {
|
||
|
|
||
|
public:
|
||
|
|
||
|
Deserializer(Stream& stream);
|
||
|
|
||
|
template <typename... T>
|
||
|
SizeType operator()(T&&... items);
|
||
|
|
||
|
private:
|
||
|
|
||
|
Stream& _stream;
|
||
|
|
||
|
// Function: _variant_helper
|
||
|
template <
|
||
|
size_t I = 0, typename... ArgsT,
|
||
|
std::enable_if_t<I==sizeof...(ArgsT)>* = nullptr
|
||
|
>
|
||
|
SizeType _variant_helper(size_t, std::variant<ArgsT...>&);
|
||
|
|
||
|
// Function: _variant_helper
|
||
|
template <
|
||
|
size_t I = 0, typename... ArgsT,
|
||
|
std::enable_if_t<I<sizeof...(ArgsT)>* = nullptr
|
||
|
>
|
||
|
SizeType _variant_helper(size_t, std::variant<ArgsT...>&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_arithmetic_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_basic_string_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_vector_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<
|
||
|
is_std_deque_v<std::decay_t<T>> ||
|
||
|
is_std_list_v<std::decay_t<T>> ||
|
||
|
is_std_forward_list_v<std::decay_t<T>>,
|
||
|
void
|
||
|
>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_map_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_unordered_map_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_set_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_unordered_set_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_enum_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_duration_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_time_point_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_optional_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_variant_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_tuple_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_array_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
|
||
|
template <typename T,
|
||
|
std::enable_if_t<!is_default_deserializable_v<std::decay_t<T>>, void>* = nullptr
|
||
|
>
|
||
|
SizeType _load(T&&);
|
||
|
};
|
||
|
|
||
|
// Constructor
|
||
|
template <typename Stream, typename SizeType>
|
||
|
Deserializer<Stream, SizeType>::Deserializer(Stream& stream) : _stream(stream) {
|
||
|
}
|
||
|
|
||
|
// Operator ()
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename... T>
|
||
|
SizeType Deserializer<Stream, SizeType>::operator() (T&&... items) {
|
||
|
return (_load(std::forward<T>(items)) + ...);
|
||
|
}
|
||
|
|
||
|
// Function: _variant_helper
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <size_t I, typename... ArgsT, std::enable_if_t<I==sizeof...(ArgsT)>*>
|
||
|
SizeType Deserializer<Stream, SizeType>::_variant_helper(size_t, std::variant<ArgsT...>&) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Function: _variant_helper
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <size_t I, typename... ArgsT, std::enable_if_t<I<sizeof...(ArgsT)>*>
|
||
|
SizeType Deserializer<Stream, SizeType>::_variant_helper(size_t i, std::variant<ArgsT...>& v) {
|
||
|
if(i == 0) {
|
||
|
using type = ExtractType_t<I, std::variant<ArgsT...>>;
|
||
|
if(v.index() != I) {
|
||
|
static_assert(
|
||
|
std::is_default_constructible<type>::value,
|
||
|
"Failed to archive variant (type should be default constructible T())"
|
||
|
);
|
||
|
v = type();
|
||
|
}
|
||
|
return _load(*std::get_if<type>(&v));
|
||
|
}
|
||
|
return _variant_helper<I+1, ArgsT...>(i-1, v);
|
||
|
}
|
||
|
|
||
|
// arithmetic data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_arithmetic_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
_stream.read(reinterpret_cast<char*>(std::addressof(t)), sizeof(t));
|
||
|
return sizeof(t);
|
||
|
}
|
||
|
|
||
|
// std::basic_string
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_basic_string_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
typename U::size_type num_chars;
|
||
|
auto sz = _load(make_size_tag(num_chars));
|
||
|
t.resize(num_chars);
|
||
|
_stream.read(reinterpret_cast<char*>(t.data()), num_chars*sizeof(typename U::value_type));
|
||
|
return sz + num_chars*sizeof(typename U::value_type);
|
||
|
}
|
||
|
|
||
|
// std::vector
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_vector_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
typename U::size_type num_data;
|
||
|
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
if constexpr(std::is_arithmetic_v<typename U::value_type>) {
|
||
|
t.resize(num_data);
|
||
|
_stream.read(reinterpret_cast<char*>(t.data()), num_data * sizeof(typename U::value_type));
|
||
|
sz += num_data * sizeof(typename U::value_type);
|
||
|
}
|
||
|
else {
|
||
|
t.resize(num_data);
|
||
|
for(auto && v : t) {
|
||
|
sz += _load(v);
|
||
|
}
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::list and std::deque
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_deque_v<std::decay_t<T>> ||
|
||
|
is_std_list_v<std::decay_t<T>> ||
|
||
|
is_std_forward_list_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
typename U::size_type num_data;
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
t.resize(num_data);
|
||
|
for(auto && v : t) {
|
||
|
sz += _load(v);
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::map
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_map_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
typename U::size_type num_data;
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
t.clear();
|
||
|
auto hint = t.begin();
|
||
|
|
||
|
typename U::key_type k;
|
||
|
typename U::mapped_type v;
|
||
|
|
||
|
for(size_t i=0; i<num_data; ++i) {
|
||
|
sz += _load(make_kv_pair(k, v));
|
||
|
hint = t.emplace_hint(hint, std::move(k), std::move(v));
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::unordered_map
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_unordered_map_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
typename U::size_type num_data;
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
t.clear();
|
||
|
t.reserve(num_data);
|
||
|
|
||
|
typename U::key_type k;
|
||
|
typename U::mapped_type v;
|
||
|
|
||
|
for(size_t i=0; i<num_data; ++i) {
|
||
|
sz += _load(make_kv_pair(k, v));
|
||
|
t.emplace(std::move(k), std::move(v));
|
||
|
}
|
||
|
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::set
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_set_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
typename U::size_type num_data;
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
t.clear();
|
||
|
auto hint = t.begin();
|
||
|
|
||
|
typename U::key_type k;
|
||
|
|
||
|
for(size_t i=0; i<num_data; ++i) {
|
||
|
sz += _load(k);
|
||
|
hint = t.emplace_hint(hint, std::move(k));
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// std::unordered_set
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_unordered_set_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
typename U::size_type num_data;
|
||
|
auto sz = _load(make_size_tag(num_data));
|
||
|
|
||
|
t.clear();
|
||
|
t.reserve(num_data);
|
||
|
|
||
|
typename U::key_type k;
|
||
|
|
||
|
for(size_t i=0; i<num_data; ++i) {
|
||
|
sz += _load(k);
|
||
|
t.emplace(std::move(k));
|
||
|
}
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// enum data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<std::is_enum_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
std::underlying_type_t<U> k;
|
||
|
auto sz = _load(k);
|
||
|
t = static_cast<U>(k);
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// duration data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_duration_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
typename U::rep count;
|
||
|
auto s = _load(count);
|
||
|
t = U{count};
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// time point data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_time_point_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
using U = std::decay_t<T>;
|
||
|
typename U::duration elapsed;
|
||
|
auto s = _load(elapsed);
|
||
|
t = U{elapsed};
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// optional data type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_optional_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
bool has_value;
|
||
|
auto s = _load(has_value);
|
||
|
if(has_value) {
|
||
|
if(!t) {
|
||
|
t = typename U::value_type();
|
||
|
}
|
||
|
s += _load(*t);
|
||
|
}
|
||
|
else {
|
||
|
t.reset();
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// variant type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_variant_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
std::decay_t<decltype(t.index())> idx;
|
||
|
auto s = _load(idx);
|
||
|
return s + _variant_helper(idx, t);
|
||
|
}
|
||
|
|
||
|
// tuple type
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_tuple_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
return std::apply(
|
||
|
[&] (auto&&... args) {
|
||
|
return (_load(std::forward<decltype(args)>(args)) + ... + 0);
|
||
|
},
|
||
|
std::forward<T>(t)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// array
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<is_std_array_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
|
||
|
using U = std::decay_t<T>;
|
||
|
|
||
|
static_assert(std::tuple_size<U>::value > 0, "Array size can't be zero");
|
||
|
|
||
|
SizeType sz;
|
||
|
|
||
|
if constexpr(std::is_arithmetic_v<typename U::value_type>) {
|
||
|
_stream.read(reinterpret_cast<char*>(t.data()), sizeof(t));
|
||
|
sz = sizeof(t);
|
||
|
}
|
||
|
else {
|
||
|
sz = 0;
|
||
|
for(auto && v : t) {
|
||
|
sz += _load(v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
// custom save method
|
||
|
template <typename Stream, typename SizeType>
|
||
|
template <typename T,
|
||
|
std::enable_if_t<!is_default_deserializable_v<std::decay_t<T>>, void>*
|
||
|
>
|
||
|
SizeType Deserializer<Stream, SizeType>::_load(T&& t) {
|
||
|
return t.load(*this);
|
||
|
}
|
||
|
|
||
|
} // end of namespace tf -----------------------------------------------------
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|