...
This commit is contained in:
518
common/adc_serialization.h
Normal file
518
common/adc_serialization.h
Normal file
@@ -0,0 +1,518 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/*
|
||||
|
||||
ABSTRACT DEVICE COMPONENTS LIBRARY
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <ranges>
|
||||
|
||||
#include "adc_traits.h"
|
||||
#include "adc_utils.h"
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
/* SERIALIZATION/DESERIALIZATION PROCESS ERROR */
|
||||
|
||||
template <typename T>
|
||||
concept adc_serialization_error_c = std::default_initializable<T> && (std::convertible_to<T, bool> || requires {
|
||||
(bool)T() == false; // default constucted value must be a "non-error"!
|
||||
});
|
||||
|
||||
// default
|
||||
typedef std::error_code adc_serialization_error_t;
|
||||
|
||||
/* SERIALIZATION/DESERIALIZATION PROCESS TUNING PARAMETERS */
|
||||
|
||||
|
||||
// delimiter between items of serializing values sequence
|
||||
static constexpr std::string_view adc_SERIALIZING_DEFAULT_SEQ_DELIMITER{";"};
|
||||
|
||||
// delimiter between items of aggregative (multi-element) serializing value
|
||||
static constexpr std::string_view adc_SERIALIZING_DEFAULT_ELEM_DELIMITER{","};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept adc_serialization_params_c = std::copyable<T> && requires(T t) {
|
||||
// delimiter between items of serializing values sequence
|
||||
requires traits::adc_output_char_range<decltype(t.elem_delim)>;
|
||||
|
||||
// delimiter between items of aggregative (multi-element) serializing value
|
||||
requires traits::adc_output_char_range<decltype(t.seq_delim)>;
|
||||
|
||||
// a format string for std::chrono::time_point types
|
||||
requires traits::adc_output_char_range<decltype(t.timepoint_format)>;
|
||||
};
|
||||
|
||||
|
||||
// default serializatio/deserialization process parameters type
|
||||
struct adc_serialization_params_t {
|
||||
std::string elem_delim{adc_SERIALIZING_DEFAULT_ELEM_DELIMITER};
|
||||
std::string seq_delim{adc_SERIALIZING_DEFAULT_SEQ_DELIMITER};
|
||||
std::string timepoint_format{"{:%FT%T}"};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* SERIALIZER/DESERIALIZER CONCEPTS */
|
||||
|
||||
template <adc_serialization_error_c RetT>
|
||||
struct adc_serializer_interface_t {
|
||||
virtual ~adc_serializer_interface_t() = default;
|
||||
|
||||
typedef RetT error_t;
|
||||
|
||||
template <std::derived_from<adc_serializer_interface_t> SelfT, traits::adc_output_char_range R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R& output, ValueT const& value)
|
||||
{
|
||||
return std::forward<SelfT>(self)(output, value);
|
||||
}
|
||||
|
||||
template <std::derived_from<adc_serializer_interface_t> SelfT, traits::adc_output_char_range R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R& output, ValueT const& value, adc_serialization_params_c auto const& params)
|
||||
{
|
||||
return std::forward<SelfT>(self)(output, value, params);
|
||||
}
|
||||
|
||||
protected:
|
||||
adc_serializer_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept adc_serializer_c =
|
||||
std::derived_from<T, adc_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 <adc_serialization_error_c RetT>
|
||||
struct adc_deserializer_interface_t {
|
||||
virtual ~adc_deserializer_interface_t() = default;
|
||||
|
||||
typedef RetT error_t;
|
||||
|
||||
template <std::derived_from<adc_deserializer_interface_t> SelfT, traits::adc_input_char_range R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R const& input, ValueT& value)
|
||||
{
|
||||
return std::forward<SelfT>(self)(input, value);
|
||||
}
|
||||
|
||||
template <std::derived_from<adc_deserializer_interface_t> SelfT, traits::adc_input_char_range R, typename ValueT>
|
||||
RetT operator()(this SelfT&& self, R const& input, ValueT& value, adc_serialization_params_c auto& params)
|
||||
{
|
||||
return std::forward<SelfT>(self)(input, value, params);
|
||||
}
|
||||
|
||||
protected:
|
||||
adc_deserializer_interface_t() = default;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept adc_deserializer_c =
|
||||
std::derived_from<T, adc_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)>;
|
||||
};
|
||||
|
||||
} // namespace adc
|
||||
|
||||
|
||||
|
||||
/* BASIC IMPLEMENTATION OF SERIALIZER AND DESERIALIZER CLASSES */
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
enum class AdcSerializerErrorCode : int { ERROR_OK, ERROR_UNDERLYING_SERIALIZER };
|
||||
|
||||
enum class AdcDeserializerErrorCode : int { ERROR_OK, ERROR_UNDERLYING_DESERIALIZER, ERROR_INVALID_SERIALIZED_VALUE };
|
||||
|
||||
} // namespace adc
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<adc::AdcSerializerErrorCode> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<adc::AdcDeserializerErrorCode> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
// error category
|
||||
struct AdcSerializerCategory : public std::error_category {
|
||||
AdcSerializerCategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "Adc-SERIALIZER-ERR-CATEGORY";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
AdcSerializerErrorCode err = static_cast<AdcSerializerErrorCode>(ec);
|
||||
|
||||
switch (err) {
|
||||
case AdcSerializerErrorCode::ERROR_OK:
|
||||
return "OK";
|
||||
case AdcSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER:
|
||||
return "error returned by underlying serializer";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const AdcSerializerCategory& get()
|
||||
{
|
||||
static const AdcSerializerCategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// error category
|
||||
struct AdcDeserializerCategory : public std::error_category {
|
||||
AdcDeserializerCategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "Adc-DESERIALIZER-ERR-CATEGORY";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
AdcDeserializerErrorCode err = static_cast<AdcDeserializerErrorCode>(ec);
|
||||
|
||||
switch (err) {
|
||||
case AdcDeserializerErrorCode::ERROR_OK:
|
||||
return "OK";
|
||||
case AdcDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER:
|
||||
return "error returned by underlying deserializer";
|
||||
case AdcDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE:
|
||||
return "invalid serialized value";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const AdcDeserializerCategory& get()
|
||||
{
|
||||
static const AdcDeserializerCategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
|
||||
|
||||
struct AdcSerializerBase : adc_serializer_interface_t<adc_serialization_error_t> {
|
||||
virtual ~AdcSerializerBase() = default;
|
||||
|
||||
using typename adc_serializer_interface_t<adc_serialization_error_t>::error_t;
|
||||
|
||||
protected:
|
||||
AdcSerializerBase() = default;
|
||||
|
||||
static void addElemDelimiter(traits::adc_output_char_range auto& output,
|
||||
adc_serialization_params_c auto const& params)
|
||||
{
|
||||
std::format_to(std::back_inserter(output), "{}", params.elem_delim);
|
||||
}
|
||||
|
||||
|
||||
static void addSeqDelimiter(traits::adc_output_char_range auto& output,
|
||||
adc_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(adc_serializer_c auto& sr,
|
||||
R const& r,
|
||||
traits::adc_output_char_range auto& output,
|
||||
adc_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 adc_deduced_err(err, AdcSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
|
||||
}
|
||||
if (++i < N) {
|
||||
// AdcSerializerBase::addSeqDelimiter(output, params);
|
||||
AdcSerializerBase::addElemDelimiter(output, params);
|
||||
}
|
||||
}
|
||||
|
||||
return AdcSerializerErrorCode::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
|
||||
|
||||
struct AdcDeserializerBase : adc_deserializer_interface_t<adc_serialization_error_t> {
|
||||
using typename adc_deserializer_interface_t<adc_serialization_error_t>::error_t;
|
||||
|
||||
virtual ~AdcDeserializerBase() = default;
|
||||
|
||||
|
||||
protected:
|
||||
AdcDeserializerBase() = default;
|
||||
|
||||
//
|
||||
// empty == true, if the 'input' is empty or if all elements consist of only spaces
|
||||
//
|
||||
static std::vector<std::string_view> splitValueIntoElements(traits::adc_input_char_range auto const& input,
|
||||
adc_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) = utils::AdcTrimSpacesView<std::string_view, std::string_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(adc_deserializer_c auto& dsr,
|
||||
traits::adc_input_char_range auto const& input,
|
||||
R& r,
|
||||
adc_serialization_params_c auto const& params)
|
||||
{
|
||||
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
|
||||
r = R{};
|
||||
return AdcDeserializerErrorCode::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 adc_deduced_err(err, AdcDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
|
||||
}
|
||||
|
||||
if (it == r.end()) {
|
||||
if constexpr (!traits::adc_fixed_size_range<R>) {
|
||||
std::back_inserter(r) = val;
|
||||
it = r.end();
|
||||
}
|
||||
} else {
|
||||
*it = val;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return AdcDeserializerErrorCode::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION OF SERIALIZER/DESERIALIZER */
|
||||
|
||||
template <typename VT>
|
||||
struct AdcSerializer : AdcSerializerBase {
|
||||
constexpr static std::string_view serializerName{"ADC-FALLBACK-SERIALIZER"};
|
||||
|
||||
template <adc_serialization_params_c ParamsT = adc_serialization_params_t>
|
||||
error_t operator()(traits::adc_output_char_range auto& output,
|
||||
VT const& value,
|
||||
ParamsT const& params = adc_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 {
|
||||
AdcSerializer<value_t> sr;
|
||||
|
||||
return AdcSerializerBase::serializeRange<value_t>(sr, value, output, params);
|
||||
}
|
||||
} else if constexpr (traits::adc_tuple_like<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 = AdcSerializer<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return AdcSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER;
|
||||
}
|
||||
|
||||
AdcSerializerBase::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 = AdcSerializer<std::tuple_element_t<I, VT>>{}(output, std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return AdcSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER;
|
||||
}
|
||||
|
||||
return AdcSerializerErrorCode::ERROR_OK;
|
||||
}
|
||||
}(value);
|
||||
} else if constexpr (traits::adc_time_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 (traits::adc_time_duration_c<VT>) {
|
||||
std::format_to(std::back_inserter(output), "{}", value.count());
|
||||
} else if constexpr (std::formattable<VT, char>) {
|
||||
std::format_to(std::back_inserter(output), "{}", value);
|
||||
} else {
|
||||
static_assert(false, "UNSUPPORTED TYPE!!!");
|
||||
}
|
||||
|
||||
return AdcSerializerErrorCode::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename VT>
|
||||
struct AdcDeserializer : AdcDeserializerBase {
|
||||
static constexpr std::string_view deserializerName{"ADC-FALLBACK-DESERIALIZER"};
|
||||
|
||||
virtual ~AdcDeserializer() = default;
|
||||
|
||||
|
||||
template <adc_serialization_params_c ParamsT = adc_serialization_params_t>
|
||||
error_t operator()(traits::adc_input_char_range auto const& input,
|
||||
VT& value,
|
||||
ParamsT const& params = adc_serialization_params_t{})
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<VT>) {
|
||||
auto v = adc::utils::AdcFromChars<VT>(utils::AdcTrimSpaces(input));
|
||||
if (!v.has_value()) {
|
||||
return AdcDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
||||
}
|
||||
|
||||
value = v.value();
|
||||
} else if constexpr (adc::traits::adc_output_char_range<VT>) {
|
||||
VT r;
|
||||
if constexpr (traits::adc_fixed_size_range<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!!!");
|
||||
|
||||
AdcDeserializer<el_t> dsr;
|
||||
return deserializingRange<el_t>(dsr, input, value, params);
|
||||
} else if constexpr (traits::adc_tuple_like<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 =
|
||||
AdcDeserializer<std::tuple_element_t<I, VT>>{}(elems[I], std::get<I>(tp), params);
|
||||
if (err) {
|
||||
return AdcDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER;
|
||||
}
|
||||
|
||||
return self.template operator()<I + 1>(tp);
|
||||
} else {
|
||||
return AdcDeserializerErrorCode::ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return AdcDeserializerErrorCode::ERROR_OK;
|
||||
}(value);
|
||||
}
|
||||
|
||||
} else if constexpr (traits::adc_time_point_c<VT>) {
|
||||
std::istringstream ist{utils::AdcTrimSpaces<std::string>(input)};
|
||||
std::chrono::from_stream(ist, params.timepoint_format.c_str(), value);
|
||||
if (ist.fail()) {
|
||||
return AdcDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
||||
}
|
||||
} else if constexpr (traits::adc_time_duration_c<VT>) {
|
||||
typename VT::rep vd;
|
||||
|
||||
AdcDeserializer<typename VT::rep> dsr;
|
||||
|
||||
auto err = dsr(utils::AdcTrimSpaces(input), vd, params);
|
||||
if (err) {
|
||||
return adc_deduced_err(err, AdcDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
|
||||
}
|
||||
|
||||
value = VT{vd};
|
||||
} else {
|
||||
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
|
||||
}
|
||||
|
||||
|
||||
return AdcDeserializerErrorCode::ERROR_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace adc
|
||||
@@ -86,6 +86,17 @@ struct adc_char_identity {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// std::array, std::span(v_t, N), std::tuple
|
||||
template <typename R>
|
||||
concept adc_fixed_size_range = std::ranges::range<R> && requires { std::tuple_size<std::remove_cvref_t<R>>::value; };
|
||||
|
||||
// non-resizable ranges
|
||||
template <typename R>
|
||||
concept adc_non_resizable_range = std::ranges::range<R> && !requires(R r, std::size_t n) { r.resize(n); };
|
||||
|
||||
|
||||
|
||||
// deduce returned type of callable
|
||||
// template <typename T>
|
||||
// using adc_retval_t = std::invoke_result_t<std::remove_cvref_t<T>>;
|
||||
@@ -270,6 +281,13 @@ using adc_common_duration_t = adc_duration_common_type_t<std::chrono::nanosecond
|
||||
std::chrono::years>;
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept adc_time_point_c = requires {
|
||||
[]<typename ClockT, typename DurT>(std::type_identity<std::chrono::time_point<ClockT, DurT>>) {
|
||||
}(std::type_identity<std::remove_cvref_t<T>>());
|
||||
};
|
||||
|
||||
// concept for hashable types
|
||||
template <typename T>
|
||||
concept adc_hashable_c = requires(T t) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
Reference in New Issue
Block a user