#pragma once #include #include #include #include #include #include "../common/adc_traits.h" namespace adc::utils { static bool AdcIsSpace(char in) noexcept { static constexpr auto ws = {' ', '\t', '\n', '\v', '\r', '\f'}; return std::ranges::any_of(ws, [in](auto p) { return p == in; }); }; template static auto AdcTrimSpaces(R&& r) { return r | std::views::drop_while(AdcIsSpace) | std::views::reverse | std::views::drop_while(AdcIsSpace) | std::views::reverse; } template static ValueT AdcFromChars(R&& range) { 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(&*rr.begin(), &*rr.end(), v); end_ptr = &*rr.end(); } else { std::string s(rr.begin(), rr.end()); end_ptr = s.data() + s.size(); res = std::from_chars(s.data(), s.data() + s.size(), v); } if (res.ec == std::errc()) { if (res.ptr != end_ptr) { throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (invalid argument)"); } return v; } else if (res.ec == std::errc::invalid_argument) { throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (invalid argument)"); } else if (res.ec == std::errc::result_out_of_range) { throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (result out of range)"); } #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; try { 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); } } catch (const std::invalid_argument&) { throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (invalid argument)"); } catch (const std::out_of_range&) { throw std::invalid_argument( "AdcFromChars: cannot convert char-range to user-type value (result out of range)"); } 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, "AdcFromChars: CANNOT CONVERT CHAR-RANGE TO USER-TYPE VALUE (UNSUPPORTED USER-VALUE TYPE)"); // throw std::invalid_argument( // "AdcFromChars: cannot convert char-range to user-type value (unsupported user-value type)"); } return ValueT(); } /* TRIVIAL SERIALIZER/DESERIALIZER */ static const std::regex AdcIntegerRegex("^ *[+-]?\\d+\\d* *$", std::regex::ECMAScript); static const std::regex AdcRealRegex("^ *[-+]?\\d+\\d*\\.?\\d*([Ee][-+]?\\d+)? *$", std::regex::ECMAScript); 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 constexpr (traits::adc_output_char_range) { SerializedT s_val; std::format_to(std::back_inserter(s_val), "{}", std::forward(value)); return s_val; } else { // throw std::invalid_argument("trivial serializer: cannot serialize value"); static_assert(false, "TRIVIAL SERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); } } template static VT AdcTrivialDeserializer(SerializedT&& s_value) { if constexpr (std::is_convertible_v) { return static_cast(std::forward(s_value)); } else if constexpr (traits::adc_input_char_range) { return AdcFromChars(std::forward(s_value)); } else { static_assert(false, "TRIVIAL DESERIALIZER: UNSUPPORTED SERIALIZING TYPE!!!"); } return VT(); } template static auto AdcValueRangeFromCharRange(OutputR& res, InputR&& input_r, DelimR&& delim, size_t max_len = std::numeric_limits::max()) { if (max_len == 0) { return res.begin(); } using value_t = std::ranges::range_value_t; if constexpr (std::is_array_v>) { std::ranges::copy(std::views::split(std::forward(input_r), std::string_view(delim)) | std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) | std::views::transform([](auto vl) { return AdcTrivialDeserializer(vl); }) | std::views::take(max_len), std::back_inserter(res)); } else { std::ranges::copy(std::views::split(std::forward(input_r), std::forward(delim)) | std::views::filter([](const auto& r) { return !std::ranges::empty(r); }) | std::views::transform([](auto vl) { return AdcTrivialDeserializer(vl); }) | std::views::take(max_len), std::back_inserter(res)); } return res.begin(); } template static auto AdcCharRangeFromValueRange(OutputR& res, InputR&& input_r, DelimR&& delim, size_t max_len = std::numeric_limits::max()) { max_len = max_len < std::ranges::size(input_r) ? max_len : std::ranges::size(input_r); if (max_len == 0) { return res.begin(); } --max_len; if (max_len) { auto view = input_r | std::views::take(max_len) | std::views::transform([&delim](auto v) { auto r = AdcTrivialSerializer(v); std::ranges::copy(delim, std::back_inserter(r)); return r; }) | std::views::join; std::ranges::copy(view, std::back_inserter(res)); auto view1 = input_r | std::views::drop(max_len) | std::views::take(1) | std::views::transform([](auto v) { auto r = AdcTrivialSerializer(v); return r; }) | std::views::join; std::ranges::copy(view1, std::back_inserter(res)); } else { res = AdcTrivialSerializer(*std::forward(input_r).begin()); } 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 details { template class AdcDelimiter { public: template auto& setDelimiter(const DT& delimiter) { _delimiter = std::string{delimiter.begin(), delimiter.end()}; return static_cast(*this); } auto& setDelimiter(const char* delimiter) { _delimiter = std::string{delimiter}; return static_cast(*this); } protected: template AdcDelimiter(const DT& delimiter = ",") : _delimiter(delimiter.begin(), delimiter.end()) { } AdcDelimiter(const char* delimiter = ",") : _delimiter(delimiter) {} virtual ~AdcDelimiter() = default; std::string _delimiter; }; } // namespace details /* */ template class AdcDefaultSerializer : public details::AdcDelimiter> { std::string _delimiter; using base_t = details::AdcDelimiter>; public: template AdcDefaultSerializer(const DT& delimiter = ",") : base_t(delimiter) { } virtual ~AdcDefaultSerializer() = default; template SerializedT operator()(const T& value) { SerializedT res; if constexpr (traits::adc_is_tuple_v) { AdcCharRangeFromTuple(res, value, _delimiter); } else if constexpr (traits::adc_input_char_range) { AdcCharRangeFromValueRange(res, value, _delimiter); } else { res = AdcTrivialSerializer(value); } return res; } }; /* */ template class AdcDefaultDeserializer : public details::AdcDelimiter> { std::string _delimiter; using base_t = details::AdcDelimiter>; public: template AdcDefaultDeserializer(const DT& delimiter = ",") : base_t(delimiter) { } virtual ~AdcDefaultDeserializer() = default; template T deserialization(const SerializedT& value) { T res; if constexpr (traits::adc_is_tuple_v) { AdcTupleFromCharRange(res, value, _delimiter); } else if constexpr (traits::adc_output_char_range) { AdcValueRangeFromCharRange(res, value, _delimiter); } else { res = AdcTrivialDeserializer(value); } return res; } }; namespace constants { static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " "; } // namespace constants template class AdcDefaultValueConverter { public: template static SerializedT serialize(const ValueT& value) { SerializedT res; if constexpr (traits::adc_is_tuple_v) { AdcCharRangeFromTuple(res, value, DELIMITER); } else if constexpr (traits::adc_input_char_range) { AdcCharRangeFromValueRange(res, value, DELIMITER); } else { res = AdcTrivialSerializer(value); } return res; } template static ValueT deserialize(const SerializedT& svalue) { if constexpr (std::is_void_v) { return; } ValueT res; if constexpr (traits::adc_is_tuple_v) { AdcTupleFromCharRange(res, svalue, DELIMITER); } else if constexpr (traits::adc_output_char_range) { AdcValueRangeFromCharRange(res, svalue, DELIMITER); } else { res = AdcTrivialDeserializer(svalue); } return res; } }; } // namespace adc::utils