From a4d6f17114378ef0810c27449fa54c12d0392aa4 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sun, 1 Feb 2026 00:10:28 +0300 Subject: [PATCH] ... --- mcc_serializer.h | 165 +++++++++++++++++++++++++++++++++++++++++------ mcc_traits.h | 5 +- 2 files changed, 147 insertions(+), 23 deletions(-) diff --git a/mcc_serializer.h b/mcc_serializer.h index a84dff9..1bacfc7 100644 --- a/mcc_serializer.h +++ b/mcc_serializer.h @@ -1,9 +1,12 @@ #pragma once +#include #include #include "mcc_concepts.h" +#include "mcc_error.h" #include "mcc_traits.h" +#include "mcc_utils.h" namespace mcc { @@ -15,11 +18,11 @@ struct mcc_serializer_interface_t { template SelfT, traits::mcc_output_char_range R, typename ValueT, - typename FmtT = std::format_string> - requires(std::same_as> || std::same_as) - mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT&& value, FmtT fmt = "{}") + typename FmtT> + requires(std::same_as, FmtT> || std::same_as) + mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt) { - return std::forward(self).operator=(output, std::forward(value), std::move(fmt)); + return std::forward(self).operator=(output, value, std::move(fmt)); } }; @@ -28,29 +31,59 @@ concept mcc_serializer_c = std::derived_from; -template -class MccSerializer; - +/* fallback template */ template class MccSerializer : public mcc_serializer_interface_t { public: + // default delimiter static constexpr std::string_view defaultDelimiter{","}; - typedef std::error_code error_t; + typedef impl::MccError error_t; MccSerializer() = default; ~MccSerializer() = default; - template > - error_t operator()(traits::mcc_output_char_range auto& output, VT&& value, FmtT fmt = "{}") - requires std::formattable + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) { - if constexpr (std::same_as>) { - std::format_to(std::back_inserter(output), fmt, value); - } else if constexpr (std::same_as) { - std::vformat_to(std::back_inserter(output), fmt, std::make_format_args(value)); + if constexpr (std::formattable) { + if constexpr (std::is_null_pointer_v) { + std::format_to(std::back_inserter(output), "{}", value); + } else if constexpr (traits::mcc_input_char_range) { + std::format_to(std::back_inserter(output), fmt, value); + } else if constexpr (std::same_as, FmtT>) { + std::string_view sfmt{fmt.begin(), fmt.end()}; + std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); + } else { + static_assert(false, "INVALID FORMAT STRING TYPE!!!"); + } + } else if constexpr (std::convertible_to) { + return MccSerializer{}(output, static_cast(value), std::move(fmt)); + } else if constexpr (std::ranges::range) { + using value_t = std::remove_cv_t>; + + // special range (character sequence) + if constexpr (std::same_as) { + std::string str; + std::ranges::copy(value, std::back_inserter(str)); + + return MccSerializer{}(output, str, std::move(fmt)); + } else { + MccSerializer sr; + + size_t i = 0, N = std::ranges::size(value); + for (auto const& el : value) { + error_t err = sr(output, el, std::move(fmt)); + if (err) { + return err; + } + if (++i < N) { + std::format_to(std::back_inserter(output), "{}", _delimiter); + } + } + } } return {}; @@ -61,23 +94,113 @@ protected: }; -template -class MccSerializer : public mcc_serializer_interface_t +/* SPECIALIZATION FOR THE SOME CONCEPTS */ + +template + requires(mcc_angle_c || + (std::ranges::input_range && mcc_angle_c>>)) +class MccSerializer : public mcc_serializer_interface_t { public: - error_t operator()(traits::mcc_output_char_range auto& output, T&& value) - { - std::format_to(std::back_inserter(output), "{}", value.degrees()); + enum SerializedCoordFormat { + CFMT_DEGREES, // floating-point representation in degrees + CFMT_SGM_HOURS, // sexagesimal representation: HH:MM:SS.SSSSSS (hours:minutes:seconds) + CFMT_SGM_DEGS // sexagesimal representation: DD:MM:SS.SSSSS (degrees:arcmins::arcsecs) + }; - return {}; + struct SexagesimalCoordPrec { + uint8_t hour_prec = 2; // number of decimal places in hour seconds + uint8_t deg_prec = 1; // number of decimal places in arcseconds + }; + + typedef impl::MccError error_t; + + void setCoordFormat(SerializedCoordFormat format) + { + _coordFormat = format; } + + SerializedCoordFormat getCoordFormat() const + { + return _coordFormat; + } + + + void setCoordPrec(SexagesimalCoordPrec prec) + { + _coordPrec = std::move(prec); + } + + SexagesimalCoordPrec getCoordPrec() const + { + return _coordPrec; + } + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + MccSerializer>> sr; + sr.setCoordFormat(_coordFormat); + sr.setCoordPrec(_coordPrec); + + error_t err; + size_t i = 0, N = std::ranges::size(value); + for (auto const& el : value) { + err = sr(output, el, std::move(fmt)); + if (err) { + return err; + } + + if (++i < N) { + std::format_to(std::back_inserter(output), " "); + } + } + + return error_t{}; + } else { // scalar + double v = (double)value; // radians + std::string sgm; + + switch (_coordFormat) { + case MccSerializer::CFMT_DEGREES: + v *= 180.0 / std::numbers::pi; + return MccSerializer{}(output, v, std::move(fmt)); + case MccSerializer::CFMT_SGM_HOURS: + sgm = utils::rad2sxg(value, true, _coordPrec.load().hour_prec); + break; + case MccSerializer::CFMT_SGM_DEGS: + sgm = utils::rad2sxg(value, false, _coordPrec.load().deg_prec); + break; + default: + break; + } + return MccSerializer{}(output, sgm, std::move(fmt)); + } + } + +protected: + std::atomic _coordFormat{MccSerializer::CFMT_DEGREES}; + std::atomic _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}}; }; +template +class MccSerializer : public mcc_serializer_interface_t +{ +}; + + + static_assert(mcc_serializer_c>, "!!!"); static_assert(mcc_serializer_c>, "!!!"); +static_assert(std::atomic::SerializedCoordFormat>::is_always_lock_free, "!!!"); +static_assert(std::atomic::SexagesimalCoordPrec>::is_always_lock_free, "!!!"); + +static_assert(traits::mcc_formattable, "!!!"); + void f() { MccSerializer s; diff --git a/mcc_traits.h b/mcc_traits.h index 0df7363..6883a38 100644 --- a/mcc_traits.h +++ b/mcc_traits.h @@ -60,8 +60,9 @@ concept mcc_range_of_output_char_range = // https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts) template -concept mcc_formattable = - requires(T v, std::format_context ctx) { std::formatter>().format(v, ctx); }; +concept mcc_formattable = requires(T v) { [](T vv) { return std::format("{}", vv); }(v); }; +// concept mcc_formattable = +// requires(T v, std::format_context ctx) { std::formatter>().format(v, ctx); }; // from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types