#pragma once #include #include #include "mcc_concepts.h" #include "mcc_error.h" #include "mcc_traits.h" #include "mcc_utils.h" namespace mcc { struct mcc_serializer_interface_t { virtual ~mcc_serializer_interface_t() = default; template SelfT, traits::mcc_output_char_range R, typename ValueT, 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, value, std::move(fmt)); } }; template concept mcc_serializer_c = std::derived_from; /* fallback template */ template class MccSerializer : public mcc_serializer_interface_t { public: // default delimiter static constexpr std::string_view defaultDelimiter{","}; typedef impl::MccError error_t; MccSerializer() = default; ~MccSerializer() = default; template error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) { 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 {}; } protected: std::string _delimiter{defaultDelimiter}; }; /* 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: 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) }; 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; std::string str; s(str, impl::MccAngleALT{1.1}); } } // namespace mcc