From 580386459e34555b5de7b6fc756f1d94b14c60c6 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 8 Apr 2024 03:08:35 +0300 Subject: [PATCH] serializing/deserializing functions --- common/adc_traits.h | 29 ++++++- common/adc_utils.h | 144 +++++++++++++++++++++++++++------ tests/adc_valueholder_test.cpp | 40 ++++++++- 3 files changed, 187 insertions(+), 26 deletions(-) diff --git a/common/adc_traits.h b/common/adc_traits.h index f7f47ba..b5e1e91 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -21,7 +21,7 @@ namespace adc::traits // https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts) template concept formattable = - requires(T& v, std::format_context ctx) { std::formatter>().format(v, ctx); }; + requires(T v, std::format_context ctx) { std::formatter>().format(v, ctx); }; // range of char/const char @@ -37,7 +37,8 @@ concept adc_output_char_range = std::ranges::output_range; // range of char/const char template -concept adc_input_char_range = std::ranges::input_range; +concept adc_input_char_range = std::ranges::input_range; +// concept adc_input_char_range = std::ranges::input_range; // deduce returned type of callable @@ -106,4 +107,28 @@ constexpr static auto adc_pf_wrapper(T&& v, Ts&&... vs) } +// check if type T is one of types in sequence Rest (e.g. adc_is_any::value == true) + +template +struct adc_is_any_of : std::disjunction...> { +}; + +template +static inline constexpr bool adc_is_any_of_v = std::disjunction_v...>; + + + +template +struct adc_is_tuple : std::false_type { +}; + + +template +struct adc_is_tuple> : std::true_type { +}; + + +template +static inline constexpr bool adc_is_tuple_v = adc_is_tuple::value; + } // namespace adc::traits diff --git a/common/adc_utils.h b/common/adc_utils.h index e2c068e..dddb2cc 100644 --- a/common/adc_utils.h +++ b/common/adc_utils.h @@ -30,18 +30,27 @@ static auto AdcTrimSpaces(R&& r) template static ValueT AdcFromChars(R&& range) { - if constexpr (std::is_arithmetic_v) { - ValueT v; + using v_t = std::remove_cv_t; + + +#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars + if constexpr (traits::adc_is_any_v) { +#else + if constexpr (traits::adc_is_any_of_v) { +#endif + v_t v; + + auto rr = AdcTrimSpaces(range); std::from_chars_result res; const char* end_ptr; // std::from_chars needs random-access container!!! if constexpr (std::ranges::random_access_range) { - res = std::from_chars(&*range.begin(), &*range.end(), v); - end_ptr = &*range.end(); + res = std::from_chars(&*rr.begin(), &*rr.end(), v); + end_ptr = &*rr.end(); } else { - std::string s(range.begin(), range.end()); + std::string s(rr.begin(), rr.end()); end_ptr = s.data() + s.size(); res = std::from_chars(s.data(), s.data() + s.size(), v); } @@ -60,7 +69,30 @@ static ValueT AdcFromChars(R&& range) throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (result out of range)"); } - } else if (std::is_constructible_v, std::ranges::iterator_t>) { +#ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars + } else if constexpr (std::is_floating_point_v) { + v_t v; + + auto rr = AdcTrimSpaces(range); + + std::string s(rr.begin(), rr.end()); + size_t pos; + if constexpr (std::is_same_v) { + v = std::stof(s, &pos); + } else if constexpr (std::is_same_v) { + v = std::stod(s, &pos); + } else if constexpr (std::is_same_v) { + v = std::stold(s, &pos); + } + + if (pos != s.size()) { + throw std::invalid_argument( + "AdcFromChars: cannot convert char-range to user-type value (invalid argument)"); + } + + return v; +#endif + } else if constexpr (std::is_constructible_v, std::ranges::iterator_t>) { return ValueT(range.begin(), range.end()); } else { static_assert(false, @@ -68,6 +100,8 @@ static ValueT AdcFromChars(R&& range) // throw std::invalid_argument( // "AdcFromChars: cannot convert char-range to user-type value (unsupported user-value type)"); } + + return ValueT(); } @@ -76,14 +110,14 @@ static ValueT AdcFromChars(R&& range) static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript); static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript); -template +template static SerializedT AdcTrivialSerializer(VT&& value) { using v_t = std::decay_t; if constexpr (std::is_convertible_v) { return static_cast(std::forward(value)); - } else if (traits::adc_output_char_range) { + } else if constexpr (traits::adc_output_char_range) { SerializedT s_val; std::format_to(std::back_inserter(s_val), "{}", std::forward(value)); return s_val; @@ -99,7 +133,7 @@ static VT AdcTrivialDeserializer(SerializedT&& s_value) { if constexpr (std::is_convertible_v) { return static_cast(std::forward(s_value)); - } else if (traits::adc_input_char_range) { + } else if constexpr (traits::adc_input_char_range) { return AdcFromChars(std::forward(s_value)); } else { static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); @@ -108,14 +142,13 @@ static VT AdcTrivialDeserializer(SerializedT&& s_value) template -OutputR AdcValueRangeFromCharRange(InputR&& input_r, - DelimR&& delim, - size_t max_len = std::numeric_limits::max()) +static auto AdcValueRangeFromCharRange(OutputR& res, + InputR&& input_r, + DelimR&& delim, + size_t max_len = std::numeric_limits::max()) { - OutputR res; - if (max_len == 0) { - return res; + return res.begin(); } using value_t = std::ranges::range_value_t; @@ -134,21 +167,20 @@ OutputR AdcValueRangeFromCharRange(InputR&& input_r, std::back_inserter(res)); } - return res; + return res.begin(); } template -OutputR AdcCharRangeFromValueRange(InputR&& input_r, - DelimR&& delim, - size_t max_len = std::numeric_limits::max()) +static auto AdcCharRangeFromValueRange(OutputR& res, + InputR&& input_r, + DelimR&& delim, + size_t max_len = std::numeric_limits::max()) { - OutputR res; - max_len = max_len < std::ranges::size(input_r) ? max_len : std::ranges::size(input_r); if (max_len == 0) { - return res; + return res.begin(); } --max_len; @@ -173,7 +205,73 @@ OutputR AdcCharRangeFromValueRange(InputR&& input_r, res = AdcTrivialSerializer(*std::forward(input_r).begin()); } - return res; + return res.begin(); } + +namespace details +{ + +template +void setTupleElements(std::tuple& tp, auto&& r) +{ + if (I < std::ranges::distance(r)) { + if constexpr (I < sizeof...(Ts)) { + std::get(tp) = + AdcFromChars>>(*(r | std::views::drop(I)).begin()); + + setTupleElements(tp, std::forward(r)); + } + } +} + + +template +void fromTupleElements(const std::tuple& tp, auto& r, const auto& delim) +{ + using r_t = std::decay_t; + + if constexpr (I < (sizeof...(Ts) - 1)) { + auto el = AdcTrivialSerializer(std::get(tp)); + std::ranges::copy(el, std::back_inserter(r)); + std::ranges::copy(delim, std::back_inserter(r)); + + fromTupleElements(tp, r, delim); + } else if constexpr (I == (sizeof...(Ts) - 1)) { + auto el = AdcTrivialSerializer(std::get(tp)); + std::ranges::copy(el, std::back_inserter(r)); + } +} + +} // namespace details + + +template +static void AdcTupleFromCharRange(std::tuple& tp, InputR&& input_r, DelimR&& delim) +{ + if constexpr (std::is_array_v>) { + auto r = std::views::split(std::forward(input_r), std::string_view(delim)) | + std::views::filter([](const auto& r) { return !std::ranges::empty(r); }); + details::setTupleElements(tp, r); + } else { + auto r = std::views::split(std::forward(input_r), std::forward(delim)) | + std::views::filter([](const auto& r) { return !std::ranges::empty(r); }); + details::setTupleElements(tp, r); + } +} + + +template +static auto AdcCharRangeFromTuple(OutputR& res, const std::tuple& tp, DelimR&& delim) +{ + if constexpr (sizeof...(Ts) > 1) { + details::fromTupleElements(tp, res, delim); + } else { + res = AdcTrivialSerializer(std::get<0>(tp)); + } + + return res.begin(); +} + + } // namespace adc::utils diff --git a/tests/adc_valueholder_test.cpp b/tests/adc_valueholder_test.cpp index 24f6f50..d7a16df 100644 --- a/tests/adc_valueholder_test.cpp +++ b/tests/adc_valueholder_test.cpp @@ -1,8 +1,9 @@ +#include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include - +#include "../common/adc_utils.h" #include "../common/adc_value_holder.h" TEST_CASE("[ADC VALUEHOLDER]") @@ -83,4 +84,41 @@ TEST_CASE("[ADC VALUEHOLDER]") using namespace std::literals; CHECK_THROWS_WITH_AS(vsh = ""s, "user value is not valid", std::system_error); + + std::string ss = "\t\t1, 2, 3, 4,5 "; + + std::vector vv; + adc::utils::AdcValueRangeFromCharRange(vv, ss, ","); + + for (auto v : vv) { + std::cout << "v = " << v << "\n"; + } + + std::list ll; + adc::utils::AdcCharRangeFromValueRange(ll, vv, "<>"); + for (auto ch : ll) { + std::cout << ch; + } + std::cout << "\n"; + + ss.clear(); + adc::utils::AdcCharRangeFromValueRange(ss, vv, "<>"); + std::cout << "STR: [" << ss << "]\n"; + + + std::tuple tp{7, 77.77}; + ss = "3, 33.33"; + adc::utils::AdcTupleFromCharRange(tp, ss, ","); + + std::cout << "TP: {" << std::get<0>(tp) << ", " << std::get<1>(tp) << "}\n"; + + std::get<1>(tp) = 77.77; + + ll.clear(); + adc::utils::AdcCharRangeFromTuple(ll, tp, "::"); + + for (auto ch : ll) { + std::cout << ch; + } + std::cout << "\n"; }