...
This commit is contained in:
@@ -7,13 +7,15 @@
|
||||
namespace snplib
|
||||
{
|
||||
|
||||
// type T is hashable
|
||||
/* type T is hashable */
|
||||
|
||||
template <typename T>
|
||||
concept snplib_hashable_c = requires(T t) {
|
||||
{ std::hash<T>{}(t) } -> std::convertible_to<size_t>;
|
||||
};
|
||||
|
||||
// some concepts for various types of char sequence
|
||||
/* some concepts for various types of char sequence */
|
||||
|
||||
template <typename T, typename CharT = char>
|
||||
concept snplib_char_range_c = std::ranges::range<T> && std::same_as<std::ranges::range_value_t<T>, CharT>;
|
||||
|
||||
@@ -27,7 +29,7 @@ template <typename T, typename CharT = char>
|
||||
concept snplib_char_view_c = std::ranges::view<T> && std::same_as<std::ranges::range_value_t<T>, CharT>;
|
||||
|
||||
|
||||
// std::chrono related concepts
|
||||
/* std::chrono related concepts */
|
||||
|
||||
template <typename T>
|
||||
concept snplib_time_duration_c = requires(T t) {
|
||||
@@ -50,11 +52,104 @@ concept snplib_systime_point_c = snplib_time_point_c<T, std::chrono::system_cloc
|
||||
template <typename T>
|
||||
concept snplib_utctime_point_c = snplib_time_point_c<T, std::chrono::utc_clock>;
|
||||
|
||||
template <typename T>
|
||||
concept snplib_anytime_point_c =
|
||||
snplib_systime_point_c<T> || snplib_utctime_point_c<T> || snplib_time_point_c<T, std::chrono::tai_clock> ||
|
||||
snplib_time_point_c<T, std::chrono::gps_clock> || snplib_time_point_c<T, std::chrono::file_clock> ||
|
||||
snplib_time_point_c<T, std::chrono::local_t>;
|
||||
|
||||
// callables (does not working for templated functions/methods and generic lambdas!)
|
||||
|
||||
/* callables (NOTE: does not working for templated functions/methods and generic lambdas!) */
|
||||
|
||||
template <typename T>
|
||||
concept snplib_callable_c = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
|
||||
|
||||
|
||||
/* std::tuple-like types */
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
template <typename T, size_t... Is>
|
||||
concept snplib_get_all_indices_c = requires(T t) { (std::get<Is>(t), ...); };
|
||||
|
||||
template <typename T>
|
||||
struct snplib_check_indices_t {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
// Specialize to extract the index sequence matching the tuple size
|
||||
template <typename T>
|
||||
requires requires { typename std::tuple_size<T>::type; }
|
||||
struct snplib_check_indices_t<T> {
|
||||
static constexpr bool value = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return snplib_get_all_indices_c<T, Is...>;
|
||||
}(std::make_index_sequence<std::tuple_size<T>::value>{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool snplib_check_indices_v = snplib_check_indices_t<T>::value;
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename T>
|
||||
concept snplib_tuple_like_c = details::snplib_check_indices_v<std::remove_cvref_t<T>>;
|
||||
|
||||
|
||||
/* fixed-size and non-resizable ranges */
|
||||
|
||||
// std::array, std::span(v_t, N), std::tuple
|
||||
template <typename T>
|
||||
concept snplib_fixed_size_range_c =
|
||||
std::ranges::range<T> && requires { std::tuple_size<std::remove_cvref_t<T>>::value; };
|
||||
|
||||
// non-resizable ranges
|
||||
template <typename T>
|
||||
concept snplib_non_resizable_range_c = std::ranges::range<T> && !requires(T r, std::size_t n) { r.resize(n); };
|
||||
|
||||
|
||||
|
||||
/* an concept for error-type */
|
||||
|
||||
//
|
||||
// a default-constructible type wich can be converted to bool-type
|
||||
// NOTE: in the SNIPPETS LIBRARY it is assumed that default constructed
|
||||
// objects of the 'snplib_error_c' types are converted to 'false', i.e.:
|
||||
//
|
||||
// T v;
|
||||
// bool ok = v == false; // 'ok' must be 'true'
|
||||
//
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
concept snplib_error_c = std::constructible_from<T> && requires(T&& t) {
|
||||
static_cast<bool>(std::forward<T>(t));
|
||||
|
||||
{ !std::forward<T>(t) } -> std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
// utility function to deduce error-type
|
||||
|
||||
template <snplib_error_c ErrT, snplib_error_c FallbackErrT>
|
||||
static auto snplib_deduced_error(ErrT const& err, FallbackErrT const& fallback_err)
|
||||
{
|
||||
if constexpr (std::same_as<ErrT, FallbackErrT>) {
|
||||
return err;
|
||||
} else if constexpr (std::is_error_code_enum_v<FallbackErrT>) { // special case
|
||||
if constexpr (std::same_as<ErrT, std::error_code>) {
|
||||
return err;
|
||||
} else {
|
||||
return fallback_err;
|
||||
}
|
||||
} else if constexpr (std::is_error_condition_enum_v<FallbackErrT>) { // special case
|
||||
if constexpr (std::same_as<ErrT, std::error_condition>) {
|
||||
return err;
|
||||
} else {
|
||||
return fallback_err;
|
||||
}
|
||||
} else {
|
||||
return fallback_err;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace snplib
|
||||
@@ -36,6 +36,7 @@ protected:
|
||||
inline static std::vector<std::function<void(const HeterogenMap*, HeterogenMap*)>> _copyFunc{};
|
||||
|
||||
inline static std::vector<std::function<void(HeterogenMap*)>> _clearFunc{};
|
||||
inline static std::vector<std::function<size_t(const HeterogenMap*)>> _sizeFunc{};
|
||||
inline static std::vector<std::function<bool(KeyT const&, const HeterogenMap*)>> _eraseFunc{};
|
||||
inline static std::vector<std::function<bool(KeyT const&, const HeterogenMap*)>> _containsFunc{};
|
||||
|
||||
@@ -119,6 +120,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
size_t N = 0;
|
||||
|
||||
for (auto& func : _sizeFunc) {
|
||||
N += func(this);
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
template <typename VT>
|
||||
bool push(KeyT const& key, VT&& value)
|
||||
{
|
||||
@@ -156,6 +169,8 @@ public:
|
||||
_setter<v_t>[obj].clear();
|
||||
});
|
||||
|
||||
_sizeFunc.emplace_back([](const HeterogenMap* obj) { return _values<v_t>[obj].size(); });
|
||||
|
||||
_eraseFunc.emplace_back([](KeyT const& k, const HeterogenMap* obj) {
|
||||
bool do_delete = _values<v_t>[obj].erase(k) != 0;
|
||||
if (do_delete) {
|
||||
|
||||
518
include/snipplib/serialization/snplib_serialization.h
Normal file
518
include/snipplib/serialization/snplib_serialization.h
Normal file
@@ -0,0 +1,518 @@
|
||||
#pragma once
|
||||
|
||||
#include "../concepts/snplib_concepts.h"
|
||||
#include "snipplib/utils/snplib_string.h"
|
||||
|
||||
namespace snplib
|
||||
{
|
||||
|
||||
|
||||
/* SERIALIZATION/DESERIALIZATION PROCESS ERROR TYPE */
|
||||
|
||||
typedef std::error_code snplib_serialization_error_t;
|
||||
|
||||
/* SERIALIZATION/DESERIALIZATION PROCESS TUNING PARAMETERS */
|
||||
|
||||
|
||||
// delimiter between items of serializing values sequence
|
||||
static constexpr std::string_view SNPLIB_SERIALIZING_DEFAULT_SEQ_DELIMITER{";"};
|
||||
|
||||
// delimiter between items of aggregative (multi-element) serializing value
|
||||
static constexpr std::string_view SNPLIB_SERIALIZING_DEFAULT_ELEM_DELIMITER{","};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept snplib_serialization_params_c = std::copyable<T> && requires(T t) {
|
||||
// delimiter between items of serializing values sequence
|
||||
requires snplib_output_char_range_c<decltype(t.elem_delim)>;
|
||||
|
||||
// delimiter between items of aggregative (multi-element) serializing value
|
||||
requires snplib_output_char_range_c<decltype(t.seq_delim)>;
|
||||
|
||||
// a format string for std::chrono::time_point types
|
||||
requires snplib_output_char_range_c<decltype(t.timepoint_format)>;
|
||||
};
|
||||
|
||||
|
||||
// default serializatio/deserialization process parameters type
|
||||
struct snplib_serialization_params_t {
|
||||
std::string elem_delim{SNPLIB_SERIALIZING_DEFAULT_ELEM_DELIMITER};
|
||||
std::string seq_delim{SNPLIB_SERIALIZING_DEFAULT_SEQ_DELIMITER};
|
||||
|
||||
std::string integer_format{"{:d}"};
|
||||
std::string real_format{"{:g}"};
|
||||
|
||||
std::string timepoint_format{"{:%FT%T}"};
|
||||
};
|
||||
|
||||
|
||||
/* SERIALIZER/DESERIALIZER CONCEPTS */
|
||||
|
||||
template <snplib_error_c RetT>
|
||||
struct snplib_serializer_interface_t {
|
||||
virtual ~snplib_serializer_interface_t() = default;
|
||||
|
||||
typedef RetT error_t;
|
||||
|
||||
template <std::derived_from<snplib_serializer_interface_t> SelfT, snplib_output_char_range_c R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R& output, ValueT const& value)
|
||||
{
|
||||
return std::forward<SelfT>(self)(output, value);
|
||||
}
|
||||
|
||||
template <std::derived_from<snplib_serializer_interface_t> SelfT, snplib_output_char_range_c R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R& output, ValueT const& value, snplib_serialization_params_c auto const& params)
|
||||
{
|
||||
return std::forward<SelfT>(self)(output, value, params);
|
||||
}
|
||||
|
||||
protected:
|
||||
snplib_serializer_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept snplib_serializer_c =
|
||||
std::derived_from<T, snplib_serializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
|
||||
// static const variable with name of the serializer
|
||||
requires std::formattable<decltype(T::serializerName), char> && std::is_const_v<decltype(T::serializerName)>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <snplib_error_c RetT>
|
||||
struct snplib_deserializer_interface_t {
|
||||
virtual ~snplib_deserializer_interface_t() = default;
|
||||
|
||||
typedef RetT error_t;
|
||||
|
||||
template <std::derived_from<snplib_deserializer_interface_t> SelfT, snplib_input_char_range_c R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R const& input, ValueT& value)
|
||||
{
|
||||
return std::forward<SelfT>(self)(input, value);
|
||||
}
|
||||
|
||||
template <std::derived_from<snplib_deserializer_interface_t> SelfT, snplib_input_char_range_c R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R const& input, ValueT& value, snplib_serialization_params_c auto& params)
|
||||
{
|
||||
return std::forward<SelfT>(self)(input, value, params);
|
||||
}
|
||||
|
||||
protected:
|
||||
snplib_deserializer_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept snplib_deserializer_c =
|
||||
std::derived_from<T, snplib_deserializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
|
||||
// static const variable with name of the deserializer
|
||||
requires std::formattable<decltype(T::deserializerName), char> &&
|
||||
std::is_const_v<decltype(T::deserializerName)>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* SERIALIZATION/DESERIALIZATION PROCESS ERROR CODES */
|
||||
|
||||
enum class snplib_serializer_error_e : int { ERROR_OK, ERROR_UNDERLYING_SERIALIZER };
|
||||
|
||||
enum class snplib_deserializer_error_e : int {
|
||||
ERROR_OK,
|
||||
ERROR_UNDERLYING_DESERIALIZER,
|
||||
ERROR_INVALID_SERIALIZED_VALUE
|
||||
};
|
||||
|
||||
} // end of namespace snplib
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<snplib::snplib_serializer_error_e> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<snplib::snplib_deserializer_error_e> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // end of namespace std
|
||||
|
||||
|
||||
|
||||
namespace snplib
|
||||
{
|
||||
|
||||
|
||||
/* BASIC IMPLEMENTATION OF SERIALIZER AND DESERIALIZER CLASSES */
|
||||
|
||||
|
||||
/* error categories */
|
||||
|
||||
struct snplib_serializer_error_category_t : public std::error_category {
|
||||
snplib_serializer_error_category_t() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "SNPLIB-SERIALIZER-ERR-CATEGORY";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
snplib_serializer_error_e err = static_cast<snplib_serializer_error_e>(ec);
|
||||
|
||||
switch (err) {
|
||||
case snplib_serializer_error_e::ERROR_OK:
|
||||
return "OK";
|
||||
case snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER:
|
||||
return "error returned by underlying serializer";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const snplib_serializer_error_category_t& get()
|
||||
{
|
||||
static const snplib_serializer_error_category_t constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct snplib_deserializer_error_category_t : public std::error_category {
|
||||
snplib_deserializer_error_category_t() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "SNPLIB-DESERIALIZER-ERR-CATEGORY";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
snplib_deserializer_error_e err = static_cast<snplib_deserializer_error_e>(ec);
|
||||
|
||||
switch (err) {
|
||||
case snplib_deserializer_error_e::ERROR_OK:
|
||||
return "OK";
|
||||
case snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER:
|
||||
return "error returned by underlying deserializer";
|
||||
case snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE:
|
||||
return "invalid serialized value";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const snplib_deserializer_error_category_t& get()
|
||||
{
|
||||
static const snplib_deserializer_error_category_t constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
|
||||
|
||||
struct snplib_serializer_base_t : snplib_serializer_interface_t<snplib_serialization_error_t> {
|
||||
virtual ~snplib_serializer_base_t() = default;
|
||||
|
||||
using typename snplib_serializer_interface_t<snplib_serialization_error_t>::error_t;
|
||||
|
||||
protected:
|
||||
snplib_serializer_base_t() = default;
|
||||
|
||||
static void addElemDelimiter(snplib_output_char_range_c auto& output,
|
||||
snplib_serialization_params_c auto const& params)
|
||||
{
|
||||
std::format_to(std::back_inserter(output), "{}", params.elem_delim);
|
||||
}
|
||||
|
||||
|
||||
static void addSeqDelimiter(snplib_output_char_range_c auto& output,
|
||||
snplib_serialization_params_c auto const& params)
|
||||
{
|
||||
std::format_to(std::back_inserter(output), "{}", params.seq_delim);
|
||||
}
|
||||
|
||||
|
||||
template <typename VT, typename R>
|
||||
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
|
||||
static error_t serializeRange(snplib_serializer_c auto& sr,
|
||||
R const& r,
|
||||
snplib_output_char_range_c auto& output,
|
||||
snplib_serialization_params_c auto const& params)
|
||||
{
|
||||
size_t i = 0, N = std::ranges::size(r);
|
||||
|
||||
for (auto const& el : r) {
|
||||
auto err = sr(output, el, params);
|
||||
if (err) {
|
||||
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
|
||||
}
|
||||
if (++i < N) {
|
||||
snplib_serializer_base_t::addElemDelimiter(output, params);
|
||||
}
|
||||
}
|
||||
|
||||
return snplib_serializer_error_e::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
|
||||
|
||||
|
||||
struct snplib_deserializer_base_t : snplib_deserializer_interface_t<snplib_serialization_error_t> {
|
||||
using typename snplib_deserializer_interface_t<snplib_serialization_error_t>::error_t;
|
||||
|
||||
virtual ~snplib_deserializer_base_t() = default;
|
||||
|
||||
|
||||
protected:
|
||||
snplib_deserializer_base_t() = default;
|
||||
|
||||
//
|
||||
// empty == true, if the 'input' is empty or if all elements consist of only spaces
|
||||
//
|
||||
static std::vector<std::string_view> splitValueIntoElements(snplib_input_char_range_c auto const& input,
|
||||
snplib_serialization_params_c auto const& params,
|
||||
bool& empty)
|
||||
{
|
||||
static_assert(std::ranges::contiguous_range<decltype(input)>, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!");
|
||||
|
||||
std::vector<std::string_view> res;
|
||||
|
||||
if (std::ranges::size(input)) {
|
||||
empty = true;
|
||||
|
||||
std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) {
|
||||
std::back_inserter(res) = snplib_trim_spaces_as_view(std::string_view{el.begin(), el.end()});
|
||||
if (empty && res.back().size()) {
|
||||
empty = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
empty = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
|
||||
static error_t deserializingRange(snplib_deserializer_c auto& dsr,
|
||||
snplib_input_char_range_c auto const& input,
|
||||
R& r,
|
||||
snplib_serialization_params_c auto const& params)
|
||||
{
|
||||
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
|
||||
r = R{};
|
||||
return snplib_deserializer_error_e::ERROR_OK;
|
||||
}
|
||||
|
||||
// auto r_str = std::views::split(input, params.seq_delim);
|
||||
auto r_str = std::views::split(input, params.elem_delim);
|
||||
VT val;
|
||||
|
||||
auto it = r.begin();
|
||||
for (auto const& el : r_str) {
|
||||
// auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
|
||||
auto err = dsr(el, val, params);
|
||||
if (err) {
|
||||
return snplib_deduced_error(err, snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
|
||||
}
|
||||
|
||||
if (it == r.end()) {
|
||||
if constexpr (!snplib_fixed_size_range_c<R>) {
|
||||
std::back_inserter(r) = val;
|
||||
it = r.end();
|
||||
}
|
||||
} else {
|
||||
*it = val;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return snplib_deserializer_error_e::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION OF SERIALIZER/DESERIALIZER */
|
||||
|
||||
template <typename VT>
|
||||
struct snplib_serializer_t : snplib_serializer_base_t {
|
||||
constexpr static std::string_view serializerName{"SNPLIB-FALLBACK-SERIALIZER"};
|
||||
|
||||
template <snplib_serialization_params_c ParamsT = snplib_serialization_params_t>
|
||||
error_t operator()(snplib_output_char_range_c auto& output,
|
||||
VT const& value,
|
||||
ParamsT const& params = snplib_serialization_params_t{})
|
||||
{
|
||||
if constexpr (std::convertible_to<VT, std::string>) {
|
||||
std::string s = value;
|
||||
std::ranges::copy(s, std::back_inserter(output));
|
||||
} else if constexpr (std::ranges::range<VT>) {
|
||||
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
||||
|
||||
// special range (character sequence)
|
||||
if constexpr (std::same_as<value_t, char>) {
|
||||
std::ranges::copy(value, std::back_inserter(output));
|
||||
} else {
|
||||
snplib_serializer_t<value_t> sr;
|
||||
|
||||
return snplib_serializer_base_t::serializeRange<value_t>(sr, value, output, params);
|
||||
}
|
||||
} else if constexpr (snplib_tuple_like_c<VT>) {
|
||||
return [&output, ¶ms]<size_t I = 0>(this auto& self, VT& tp) -> error_t {
|
||||
if constexpr (I < (std::tuple_size_v<VT> - 1)) {
|
||||
auto err = snplib_serializer_t<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
|
||||
}
|
||||
|
||||
snplib_serializer_base_t::addElemDelimiter(output, params);
|
||||
|
||||
return self.template operator()<I + 1>(tp);
|
||||
} else if constexpr (I < (std::tuple_size_v<VT> - 1)) { // the last element
|
||||
auto err = snplib_serializer_t<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return snplib_deduced_error(err, snplib_serializer_error_e::ERROR_UNDERLYING_SERIALIZER);
|
||||
}
|
||||
|
||||
return snplib_serializer_error_e::ERROR_OK;
|
||||
}
|
||||
}(value);
|
||||
} else if constexpr (snplib_anytime_point_c<VT>) {
|
||||
std::string_view fmt{params.timepoint_format};
|
||||
std::vformat_to(std::back_inserter(output), fmt, std::make_format_args(value));
|
||||
} else if constexpr (snplib_time_duration_c<VT>) {
|
||||
std::format_to(std::back_inserter(output), "{}", value.count());
|
||||
} else if constexpr (std::integral<VT>) {
|
||||
std::vformat_to(std::back_inserter(output), std::string_view{params.integer_format},
|
||||
std::make_format_args(value));
|
||||
} else if constexpr (std::floating_point<VT>) {
|
||||
std::vformat_to(std::back_inserter(output), std::string_view{params.real_format},
|
||||
std::make_format_args(value));
|
||||
} else if constexpr (std::formattable<VT, char>) {
|
||||
std::format_to(std::back_inserter(output), "{}", value);
|
||||
} else {
|
||||
static_assert(false, "UNSUPPORTED TYPE!!!");
|
||||
}
|
||||
|
||||
return snplib_serializer_error_e::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename VT>
|
||||
struct snplib_deserializer_t : snplib_deserializer_base_t {
|
||||
static constexpr std::string_view deserializerName{"SNPLIB-FALLBACK-DESERIALIZER"};
|
||||
|
||||
virtual ~snplib_deserializer_t() = default;
|
||||
|
||||
|
||||
template <snplib_serialization_params_c ParamsT = snplib_serialization_params_t>
|
||||
error_t operator()(snplib_input_char_range_c auto const& input,
|
||||
VT& value,
|
||||
ParamsT const& params = snplib_serialization_params_t{})
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<VT>) {
|
||||
auto v = snplib_num_from_range<VT>(snplib_trim_spaces<std::string>(input));
|
||||
if (!v.has_value()) {
|
||||
return snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE;
|
||||
}
|
||||
|
||||
value = v.value();
|
||||
} else if constexpr (snplib_output_char_range_c<VT>) {
|
||||
VT r;
|
||||
if constexpr (snplib_fixed_size_range_c<VT>) {
|
||||
size_t N =
|
||||
std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input);
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
r[i] = input[i];
|
||||
}
|
||||
if (std::ranges::size(r) > N) {
|
||||
for (size_t i = N; i < std::ranges::size(r); ++i) {
|
||||
r[i] = '\0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::ranges::copy(input, std::back_inserter(r));
|
||||
}
|
||||
|
||||
value = r;
|
||||
} else if constexpr (std::ranges::range<VT>) {
|
||||
using el_t = std::ranges::range_value_t<VT>;
|
||||
|
||||
static_assert(std::ranges::output_range<VT, el_t>, "INVALID RANGE TYPE!!!");
|
||||
|
||||
// no reference or constants allowed
|
||||
static_assert(!(std::is_reference_v<el_t> || std::is_const_v<el_t>), "INVALID RANGE ELEMENT TYPE!!!");
|
||||
|
||||
snplib_deserializer_t<el_t> dsr;
|
||||
|
||||
return deserializingRange<el_t>(dsr, input, value, params);
|
||||
} else if constexpr (snplib_tuple_like_c<VT>) {
|
||||
bool empty;
|
||||
auto elems = splitValueIntoElements(input, params, &empty);
|
||||
if (empty) {
|
||||
value = VT{};
|
||||
} else {
|
||||
return [&input, ¶ms, &elems]<size_t I = 0>(this auto& self, VT& tp) {
|
||||
if constexpr (I < std::tuple_size_v<VT>) {
|
||||
if (I < elems.size()) {
|
||||
auto err =
|
||||
snplib_deserializer_t<std::tuple_element_t<I, VT>>{}(elems[I], std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return snplib_deduced_error(err,
|
||||
snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
|
||||
}
|
||||
|
||||
return self.template operator()<I + 1>(tp);
|
||||
} else {
|
||||
return snplib_deserializer_error_e::ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return snplib_deserializer_error_e::ERROR_OK;
|
||||
}(value);
|
||||
}
|
||||
|
||||
} else if constexpr (snplib_anytime_point_c<VT>) {
|
||||
std::istringstream ist{snplib_trim_spaces<std::string>(input)};
|
||||
std::chrono::from_stream(ist, params.timepoint_format.c_str(), value);
|
||||
if (ist.fail()) {
|
||||
return snplib_deserializer_error_e::ERROR_INVALID_SERIALIZED_VALUE;
|
||||
}
|
||||
} else if constexpr (snplib_time_duration_c<VT>) {
|
||||
typename VT::rep vd;
|
||||
|
||||
snplib_deserializer_t<typename VT::rep> dsr;
|
||||
|
||||
auto err = dsr(snplib_trim_spaces_as_view(input), vd, params);
|
||||
if (err) {
|
||||
return snplib_deduced_err(err, snplib_deserializer_error_e::ERROR_UNDERLYING_DESERIALIZER);
|
||||
}
|
||||
|
||||
value = VT{vd};
|
||||
} else {
|
||||
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
|
||||
}
|
||||
|
||||
|
||||
return snplib_deserializer_error_e::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // end of namespace snplib
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
#include "../concepts/snplib_concepts.h"
|
||||
|
||||
namespace snplib {
|
||||
namespace snplib
|
||||
{
|
||||
|
||||
constexpr static bool snplib_is_space(char in) noexcept
|
||||
{
|
||||
@@ -17,32 +18,37 @@ constexpr static bool snplib_is_space(char in) noexcept
|
||||
enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH };
|
||||
|
||||
template <snplib_output_char_range_c OR, snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
|
||||
constexpr static OR snplib_trim_spaces(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
constexpr static OR snplib_trim_spaces(R&& r,
|
||||
TrimType type = TrimType::TRIM_BOTH,
|
||||
IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
{
|
||||
if (type == TrimType::TRIM_LEFT) {
|
||||
auto res = r | std::views::drop_while(is_space_func);
|
||||
return OR{res.begin(), res.end()};
|
||||
} else if (type == TrimType::TRIM_RIGHT) {
|
||||
auto res = r | std::views::reverse | std::views::drop_while(is_space_func) |
|
||||
std::views::reverse;
|
||||
auto res = r | std::views::reverse | std::views::drop_while(is_space_func) | std::views::reverse;
|
||||
return OR{res.begin(), res.end()};
|
||||
} else if (type == TrimType::TRIM_BOTH) {
|
||||
auto res = r | std::views::drop_while(is_space_func) | std::views::reverse | std::views::drop_while(is_space_func) |
|
||||
std::views::reverse;
|
||||
auto res = r | std::views::drop_while(is_space_func) | std::views::reverse |
|
||||
std::views::drop_while(is_space_func) | std::views::reverse;
|
||||
return OR{res.begin(), res.end()};
|
||||
}
|
||||
|
||||
return OR{};
|
||||
}
|
||||
|
||||
return OR{};
|
||||
}
|
||||
|
||||
template <snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
|
||||
constexpr static std::string snplib_trim_spaces(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
constexpr static std::string snplib_trim_spaces(R&& r,
|
||||
TrimType type = TrimType::TRIM_BOTH,
|
||||
IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
{
|
||||
return snplib_trim_spaces<std::string>(std::forward<R>(r), type, std::forward<IsSpaceFuncT>(is_space_func));
|
||||
}
|
||||
|
||||
template <snplib_input_char_range_c R, typename IsSpaceFuncT = decltype(snplib_is_space)>
|
||||
constexpr static std::string_view snplib_trim_spaces_as_view(R&& r, TrimType type = TrimType::TRIM_BOTH, IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
constexpr static std::string_view snplib_trim_spaces_as_view(R&& r,
|
||||
TrimType type = TrimType::TRIM_BOTH,
|
||||
IsSpaceFuncT&& is_space_func = snplib_is_space)
|
||||
requires std::ranges::contiguous_range<R>
|
||||
{
|
||||
auto end = std::forward<R>(r).end();
|
||||
@@ -74,4 +80,55 @@ constexpr static std::string_view snplib_trim_spaces_as_view(R&& r, TrimType typ
|
||||
}
|
||||
|
||||
|
||||
} // end of snplib namespace
|
||||
|
||||
template <typename T, snplib_input_char_range_c R>
|
||||
static std::optional<T> snplib_num_from_range(R&& r)
|
||||
requires((std::integral<T> || std::floating_point<T>) && std::ranges::contiguous_range<R>)
|
||||
{
|
||||
T val;
|
||||
const char* end_ptr = &*r.end();
|
||||
|
||||
if constexpr (std::integral<T>) {
|
||||
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
|
||||
if (cvt_res.ec != std::errc()) {
|
||||
return std::nullopt;
|
||||
} else if (cvt_res.ptr != end_ptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars
|
||||
std::string s{str.begin(), str.end()};
|
||||
size_t pos;
|
||||
|
||||
try {
|
||||
if constexpr (std::same_as<T, float>) {
|
||||
val = std::stof(s, &pos);
|
||||
} else if constexpr (std::same_as<T, double>) {
|
||||
val = std::stod(s, &pos);
|
||||
} else {
|
||||
val = std::stold(s, &pos);
|
||||
}
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (pos != s.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val);
|
||||
|
||||
if (cvt_res.ec != std::errc()) {
|
||||
return std::nullopt;
|
||||
} else if (cvt_res.ptr != end_ptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace snplib
|
||||
@@ -1,2 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "snplib_hash.h"
|
||||
#include "snplib_string.h"
|
||||
Reference in New Issue
Block a user