From b871b9dd46d7628caa8bb4e509e20d970475d33c Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 2 Feb 2026 12:15:07 +0300 Subject: [PATCH] ... --- mcc_angle.h | 25 +++- mcc_deserializer.h | 330 ++++++++++++++++++++++++++++++++++----------- mcc_serializer.h | 72 +--------- mcc_utils.h | 82 +++++++++++ 4 files changed, 352 insertions(+), 157 deletions(-) diff --git a/mcc_angle.h b/mcc_angle.h index 87f898c..ac8a2f9 100644 --- a/mcc_angle.h +++ b/mcc_angle.h @@ -657,7 +657,7 @@ static constexpr std::string_view MCC_COORDPAIR_KIND_HADEC_OBS_STR = "HADEC-OBS" static constexpr std::string_view MCC_COORDPAIR_KIND_AZALT_STR = "AZALT"; static constexpr std::string_view MCC_COORDPAIR_KIND_AZZD_STR = "AZZD"; static constexpr std::string_view MCC_COORDPAIR_KIND_XY_STR = "XY"; -static constexpr std::string_view MCC_COORDPAIR_KIND_LATLON_STR = "LATLON"; +static constexpr std::string_view MCC_COORDPAIR_KIND_LONLAT_STR = "LATLON"; static constexpr std::string_view MCC_COORDPAIR_KIND_GENERIC_STR = "GENERIC"; static constexpr std::string_view MCC_COORDPAIR_KIND_UNKNOWN_STR = "UNKNOWN"; @@ -671,7 +671,7 @@ static constexpr std::string_view MccCoordPairKindStr = : KIND == MccCoordPairKind::COORDS_KIND_AZALT ? MCC_COORDPAIR_KIND_AZALT_STR : KIND == MccCoordPairKind::COORDS_KIND_AZZD ? MCC_COORDPAIR_KIND_AZZD_STR : KIND == MccCoordPairKind::COORDS_KIND_XY ? MCC_COORDPAIR_KIND_XY_STR - : KIND == MccCoordPairKind::COORDS_KIND_LONLAT ? MCC_COORDPAIR_KIND_LATLON_STR + : KIND == MccCoordPairKind::COORDS_KIND_LONLAT ? MCC_COORDPAIR_KIND_LONLAT_STR : KIND == MccCoordPairKind::COORDS_KIND_GENERIC ? MCC_COORDPAIR_KIND_GENERIC_STR : MCC_COORDPAIR_KIND_UNKNOWN_STR; @@ -686,7 +686,7 @@ static constexpr std::string_view MccCoordPairKindToStr(MccCoordPairKind KIND) : KIND == MccCoordPairKind::COORDS_KIND_AZALT ? MCC_COORDPAIR_KIND_AZALT_STR : KIND == MccCoordPairKind::COORDS_KIND_AZZD ? MCC_COORDPAIR_KIND_AZZD_STR : KIND == MccCoordPairKind::COORDS_KIND_XY ? MCC_COORDPAIR_KIND_XY_STR - : KIND == MccCoordPairKind::COORDS_KIND_LONLAT ? MCC_COORDPAIR_KIND_LATLON_STR + : KIND == MccCoordPairKind::COORDS_KIND_LONLAT ? MCC_COORDPAIR_KIND_LONLAT_STR : KIND == MccCoordPairKind::COORDS_KIND_GENERIC ? MCC_COORDPAIR_KIND_GENERIC_STR : MCC_COORDPAIR_KIND_UNKNOWN_STR; } @@ -709,10 +709,27 @@ static constexpr MccCoordPairKind MccCoordStrToPairKind(R&& spair) : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_AZALT_STR) ? MccCoordPairKind::COORDS_KIND_AZALT : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_AZZD_STR) ? MccCoordPairKind::COORDS_KIND_AZZD : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_XY_STR) ? MccCoordPairKind::COORDS_KIND_XY - : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_LATLON_STR) ? MccCoordPairKind::COORDS_KIND_LONLAT + : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_LONLAT_STR) ? MccCoordPairKind::COORDS_KIND_LONLAT : hash == mcc::utils::FNV1aHash(MCC_COORDPAIR_KIND_GENERIC_STR) ? MccCoordPairKind::COORDS_KIND_GENERIC : MccCoordPairKind::COORDS_KIND_UNKNOWN; } +enum class MccCoordinatePairRep : int { + MCC_COORDPAIR_REP_DEGREES, // both angles are in decimal degrees + MCC_COORDPAIR_REP_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation + MCC_COORDPAIR_REP_SXGM_DEGDEG // both angles are in sexagesimal degrees +}; + + +// default wide-acceptable sexagesimal representation +static constexpr MccCoordinatePairRep MccCoordinatePairToSxgmRep(MccCoordPairKind kind) +{ + return kind == MccCoordPairKind::COORDS_KIND_AZALT || kind == MccCoordPairKind::COORDS_KIND_AZZD || + kind == MccCoordPairKind::COORDS_KIND_XY || kind == MccCoordPairKind::COORDS_KIND_LONLAT || + kind == MccCoordPairKind::COORDS_KIND_GENERIC + ? MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_DEGDEG + : MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG; // RA-DEC or HA-DEC +} + } // namespace mcc::impl diff --git a/mcc_deserializer.h b/mcc_deserializer.h index 50722ed..d2fc59f 100644 --- a/mcc_deserializer.h +++ b/mcc_deserializer.h @@ -10,8 +10,13 @@ * * ****************************************************************************************/ +#include +#include +#include + #include "mcc_concepts.h" -#include "mcc_constants.h" +#include "mcc_coordinate.h" +#include "mcc_epoch.h" #include "mcc_error.h" namespace mcc::impl @@ -20,7 +25,7 @@ namespace mcc::impl enum class MccDeserializerErrorCode : int { ERROR_OK, ERROR_UNDERLYING_DESERIALIZER, - ERROR_INVALID_SERIAL_VALUE, + ERROR_INVALID_SERIALIZED_VALUE, ERROR_COORD_TRANSFORM }; @@ -61,7 +66,7 @@ struct MccDeserializerCategory : public std::error_category { return "OK"; case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER: return "error returned by underlying deserializer"; - case MccDeserializerErrorCode::ERROR_INVALID_SERIAL_VALUE: + case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE: return "invalid serialized value"; case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM: return "coordinates transformation error"; @@ -90,8 +95,11 @@ struct mcc_deserializer_interface_t { typedef RetT error_t; - template SelfT, traits::mcc_input_char_range R, typename ValueT> - RetT operator()(this SelfT&& self, R const& input, ValueT& value) + template SelfT, + traits::mcc_input_char_range R, + typename ValueT, + typename... DeserParamsT> + RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params) { return std::forward(self)(input, value); } @@ -107,89 +115,46 @@ concept mcc_deserializer_c = std::derived_from { +struct MccDeserializerBase : mcc_deserializer_interface_t, utils::mcc_elem_sequence_with_delim_t { using typename mcc_deserializer_interface_t::error_t; virtual ~MccDeserializerBase() = default; - // set delimiter for elements of the range-type serialized value - template - void setSeqDelimiter(R const& delim) - { - if constexpr (std::is_array_v>) { - setSeqDelimiter(std::string_view{delim}); - } else { - _seqDelimiter.clear(); - std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); - } - } - - template - R getSeqDelimiter() const - { - if constexpr (std::same_as) { - return _seqDelimiter; - } else if constexpr (std::ranges::view) { - return R{_seqDelimiter.begin(), _seqDelimiter.end()}; - } else { - R r; - std::ranges::copy(_seqDelimiter, std::back_inserter(r)); - - return r; - } - } - - std::string getSeqDelimiter() const - { - return getSeqDelimiter(); - } - - - template - void setElemDelimiter(R const& delim) - { - if constexpr (std::is_array_v>) { - setElemDelimiter(std::string_view{delim}); - } else { - _elementDelimiter.clear(); - std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); - } - } - - template - R getElemDelimiter() const - { - if constexpr (std::same_as) { - return _elementDelimiter; - } else if constexpr (std::ranges::view) { - return R{_elementDelimiter.begin(), _elementDelimiter.end()}; - } else { - R r; - std::ranges::copy(_elementDelimiter, std::back_inserter(r)); - - return r; - } - } - - std::string getElemDelimiter() const - { - return getElemDelimiter(); - } protected: MccDeserializerBase() = default; - // delimiter for sequence of serialized values - std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER}; - - // delimiter for aggregative (multi-element) serialized value - std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER}; - - template - requires(std::ranges::input_range && std::same_as>) - error_t deserializingRange(mcc_deserializer_c auto& dsr, traits::mcc_input_char_range auto const& input, R& r) const + // + // empty == true, if the 'input' is empty or if all elements consist of only spaces + // + std::vector splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const { - if (std::ranges::size(input) == 0) { // ignore empy input + std::vector res; + + if (std::ranges::size(input)) { + empty = true; + + std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&res, &empty](auto const& el) { + std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()}); + if (empty && res.back().size()) { + empty = false; + } + }); + } else { + empty = true; + } + + return res; + } + + template R, typename... DeserParamsT> + error_t deserializingRange(mcc_deserializer_c auto& dsr, + traits::mcc_input_char_range auto const& input, + R& r, + DeserParamsT&&... params) const + { + if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!! + r = R{}; return MccDeserializerErrorCode::ERROR_OK; } @@ -198,7 +163,7 @@ protected: auto it = r.begin(); for (auto const& el : r_str) { - auto err = dsr(el, val); + auto err = dsr(el, val, std::forward(params)...); if (err) { return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); } @@ -219,7 +184,8 @@ protected: } // namespace details -// +/* fallback template: basic deserializer */ + template class MccDeserializer : public details::MccDeserializerBase { @@ -234,7 +200,7 @@ public: if constexpr (std::is_arithmetic_v) { auto v = mcc::utils::numFromStr(trimSpaces(input)); if (!v.has_value()) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIAL_VALUE; + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; } value = v.value(); @@ -267,6 +233,17 @@ public: MccDeserializer dsr; return deserializingRange(dsr, input, value); + } else if constexpr (traits::mcc_time_duration_c) { + typename VT::rep vd; + + MccDeserializer dsr; + + auto err = dsr(trimSpaces(input), vd); + if (err) { + return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + value = VT{vd}; } else { static_assert(false, "UNSUPPORTED VALUE TYPE!!!"); } @@ -276,4 +253,193 @@ public: } }; + + +/* SPECIALIZATION FOR THE SOME CONCEPTS */ + + +template + requires(mcc_coord_epoch_c || (std::ranges::output_range> && + mcc_coord_epoch_c>>)) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + { + if constexpr (mcc_coord_epoch_c) { // scalar + bool ok = value.fromCharRange(input); + if (!ok) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + return deserializingRange(dsr, input, value); + } + } +}; + +template + requires(!std::is_arithmetic_v && + (mcc_angle_c || (std::ranges::output_range> && + mcc_angle_c>>))) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + // if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs + // otherwise the input sequence is interpretated as decimal or sexagesimal degrees + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false) + { + if constexpr (mcc_angle_c) { // scalar + auto res = utils::parsAngleString(input, hms); + if (res) { + value = res.value(); + } else { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + if constexpr (std::invocable, decltype(input), VT&, bool>) { + // it is assumed here, the angle deserializer has the same behavior as the one implemented above + // (that is, it can interpret the third argument as a flag "false/true = only + // degrees/hour-like") + return deserializingRange(dsr, input, value, hms); + } else { + return deserializingRange(dsr, input, value); + } + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + + +template + requires(mcc_skypoint_c || (std::ranges::output_range> && + mcc_skypoint_c>>)) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + { + if constexpr (mcc_skypoint_c) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND} + bool empty; // exactly 2 or >3 elements + auto elems = splitAggregateValue(input, empty); + + if (empty || (elems.size() < 2) || (elems.size() == 3)) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; + MccCelestialCoordEpoch epoch; // J2000.0 + + MccAngle x, y; + MccDeserializer dsr_ang; + + typename MccDeserializer::error_t dsr_err; + + if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND + // first, get pair-kind + pair_kind = MccCoordStrToPairKind(elems[3]); + if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + // epoch + bool ok = epoch.fromCharRange(elems[2]); + if (!ok) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + } + + if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS) + if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) { + // if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format + if constexpr (std::invocable, decltype(elems[0]), MccAngle&, bool>) { + // it is assumed here, the angle deserializer has the same behavior as the one implemented above + // (that is, it can interpret the third argument as a flag "false/true = only + // degrees/hour-like") + dsr_err = dsr_ang(elems[0], x, true); + } else { + dsr_err = dsr_ang(elems[0], x); + } + } else { + dsr_err = dsr_ang(elems[0], x); + } + + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + dsr_err = dsr_ang(elems[1], y); + + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + } + + + switch (pair_kind) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + value.from(MccSkyRADEC_ICRS{(double)x, (double)y}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_OBS: + value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_APP: + value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_OBS: + value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_APP: + value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZZD: + value.from(MccSkyAZZD{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZALT: + value.from(MccSkyAZALT{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_XY: + value.from(MccGenXY{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_GENERIC: + value.from(MccCoordPair{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_LONLAT: + value.from(MccGeoLONLAT{(double)x, (double)y}); + break; + default: + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + return deserializingRange(dsr, input, value); + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + } // namespace mcc::impl diff --git a/mcc_serializer.h b/mcc_serializer.h index 9d50024..dabc9c9 100644 --- a/mcc_serializer.h +++ b/mcc_serializer.h @@ -122,84 +122,14 @@ concept mcc_serializer_c = std::derived_from { +struct MccSerializerBase : mcc_serializer_interface_t, utils::mcc_elem_sequence_with_delim_t { using typename mcc_serializer_interface_t::error_t; virtual ~MccSerializerBase() = default; - // set delimiter for elements of the range-type serialized value - template - void setSeqDelimiter(R const& delim) - { - if constexpr (std::is_array_v>) { - setSeqDelimiter(std::string_view{delim}); - } else { - _seqDelimiter.clear(); - std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); - } - } - - template - R getSeqDelimiter() const - { - if constexpr (std::same_as) { - return _seqDelimiter; - } else if constexpr (std::ranges::view) { - return R{_seqDelimiter.begin(), _seqDelimiter.end()}; - } else { - R r; - std::ranges::copy(_seqDelimiter, std::back_inserter(r)); - - return r; - } - } - - std::string getSeqDelimiter() const - { - return getSeqDelimiter(); - } - - - template - void setElemDelimiter(R const& delim) - { - if constexpr (std::is_array_v>) { - setElemDelimiter(std::string_view{delim}); - } else { - _elementDelimiter.clear(); - std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); - } - } - - template - R getElemDelimiter() const - { - if constexpr (std::same_as) { - return _elementDelimiter; - } else if constexpr (std::ranges::view) { - return R{_elementDelimiter.begin(), _elementDelimiter.end()}; - } else { - R r; - std::ranges::copy(_elementDelimiter, std::back_inserter(r)); - - return r; - } - } - - std::string getElemDelimiter() const - { - return getElemDelimiter(); - } - protected: MccSerializerBase() = default; - // delimiter for sequence of serializing values - std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER}; - - // delimiter for aggregative (multi-element) serializing value - std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER}; - template requires(std::ranges::input_range && std::same_as>) error_t serializingRange(mcc_serializer_c auto& sr, diff --git a/mcc_utils.h b/mcc_utils.h index 765ee2c..7b0da5c 100644 --- a/mcc_utils.h +++ b/mcc_utils.h @@ -8,6 +8,8 @@ #include #include #include + +#include "mcc_constants.h" #include "mcc_traits.h" namespace mcc::utils @@ -758,4 +760,84 @@ protected: } }; + +/* a base class for some sequence of elements with delimiter */ + +struct mcc_elem_sequence_with_delim_t { + virtual ~mcc_elem_sequence_with_delim_t() = default; + + // set delimiter for elements of the range-type serialized value + template + void setSeqDelimiter(R const& delim) + { + if constexpr (std::is_array_v>) { + setSeqDelimiter(std::string_view{delim}); + } else { + _seqDelimiter.clear(); + std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); + } + } + + template + R getSeqDelimiter() const + { + if constexpr (std::same_as) { + return _seqDelimiter; + } else if constexpr (std::ranges::view) { + return R{_seqDelimiter.begin(), _seqDelimiter.end()}; + } else { + R r; + std::ranges::copy(_seqDelimiter, std::back_inserter(r)); + + return r; + } + } + + std::string getSeqDelimiter() const + { + return getSeqDelimiter(); + } + + + template + void setElemDelimiter(R const& delim) + { + if constexpr (std::is_array_v>) { + setElemDelimiter(std::string_view{delim}); + } else { + _elementDelimiter.clear(); + std::ranges::copy(delim, std::back_inserter(_seqDelimiter)); + } + } + + template + R getElemDelimiter() const + { + if constexpr (std::same_as) { + return _elementDelimiter; + } else if constexpr (std::ranges::view) { + return R{_elementDelimiter.begin(), _elementDelimiter.end()}; + } else { + R r; + std::ranges::copy(_elementDelimiter, std::back_inserter(r)); + + return r; + } + } + + std::string getElemDelimiter() const + { + return getElemDelimiter(); + } + +protected: + mcc_elem_sequence_with_delim_t() = default; + + // delimiter for sequence of serializing values + std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER}; + + // delimiter for aggregative (multi-element) serializing value + std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER}; +}; + } // namespace mcc::utils