This commit is contained in:
Timur A. Fatkhullin
2026-02-01 00:10:28 +03:00
parent bfd668cff2
commit a4d6f17114
2 changed files with 147 additions and 23 deletions

View File

@@ -1,9 +1,12 @@
#pragma once #pragma once
#include <atomic>
#include <concepts> #include <concepts>
#include "mcc_concepts.h" #include "mcc_concepts.h"
#include "mcc_error.h"
#include "mcc_traits.h" #include "mcc_traits.h"
#include "mcc_utils.h"
namespace mcc namespace mcc
{ {
@@ -15,11 +18,11 @@ struct mcc_serializer_interface_t {
template <std::derived_from<mcc_serializer_interface_t> SelfT, template <std::derived_from<mcc_serializer_interface_t> SelfT,
traits::mcc_output_char_range R, traits::mcc_output_char_range R,
typename ValueT, typename ValueT,
typename FmtT = std::format_string<ValueT>> typename FmtT>
requires(std::same_as<FmtT, std::format_string<ValueT>> || std::same_as<FmtT, std::string_view>) requires(std::same_as<std::format_string<ValueT>, FmtT> || std::same_as<std::string_view, FmtT>)
mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT&& value, FmtT fmt = "{}") mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt)
{ {
return std::forward<SelfT>(self).operator=(output, std::forward<ValueT>(value), std::move(fmt)); return std::forward<SelfT>(self).operator=(output, value, std::move(fmt));
} }
}; };
@@ -28,29 +31,59 @@ concept mcc_serializer_c = std::derived_from<T, mcc_serializer_interface_t>;
template <typename VT> /* fallback template */
class MccSerializer;
template <typename VT> template <typename VT>
class MccSerializer : public mcc_serializer_interface_t class MccSerializer : public mcc_serializer_interface_t
{ {
public: public:
// default delimiter
static constexpr std::string_view defaultDelimiter{","}; static constexpr std::string_view defaultDelimiter{","};
typedef std::error_code error_t; typedef impl::MccError error_t;
MccSerializer() = default; MccSerializer() = default;
~MccSerializer() = default; ~MccSerializer() = default;
template <typename FmtT = std::format_string<VT>> template <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT&& value, FmtT fmt = "{}") error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
requires std::formattable<VT, char>
{ {
if constexpr (std::same_as<FmtT, std::format_string<VT>>) { if constexpr (std::formattable<VT, char>) {
std::format_to(std::back_inserter(output), fmt, value); if constexpr (std::is_null_pointer_v<FmtT>) {
} else if constexpr (std::same_as<FmtT, std::string_view>) { std::format_to(std::back_inserter(output), "{}", value);
std::vformat_to(std::back_inserter(output), fmt, std::make_format_args(value)); } else if constexpr (traits::mcc_input_char_range<FmtT>) {
std::format_to(std::back_inserter(output), fmt, value);
} else if constexpr (std::same_as<std::format_string<VT>, 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<VT, std::string>) {
return MccSerializer<std::string>{}(output, static_cast<std::string>(value), std::move(fmt));
} else if constexpr (std::ranges::range<VT>) {
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
// special range (character sequence)
if constexpr (std::same_as<value_t, char>) {
std::string str;
std::ranges::copy(value, std::back_inserter(str));
return MccSerializer<std::string>{}(output, str, std::move(fmt));
} else {
MccSerializer<value_t> 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 {}; return {};
@@ -61,23 +94,113 @@ protected:
}; };
template <mcc_angle_c T> /* SPECIALIZATION FOR THE SOME CONCEPTS */
class MccSerializer<T> : public mcc_serializer_interface_t
template <typename VT>
requires(mcc_angle_c<VT> ||
(std::ranges::input_range<VT> && mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
class MccSerializer<VT> : public mcc_serializer_interface_t
{ {
public: public:
error_t operator()(traits::mcc_output_char_range auto& output, T&& value) enum SerializedCoordFormat {
{ CFMT_DEGREES, // floating-point representation in degrees
std::format_to(std::back_inserter(output), "{}", value.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 <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::ranges::input_range<VT>) {
MccSerializer<std::remove_cv_t<std::ranges::range_value_t<VT>>> 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<double>{}(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<std::string>{}(output, sgm, std::move(fmt));
}
}
protected:
std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_DEGREES};
std::atomic<SexagesimalCoordPrec> _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}};
}; };
template <mcc_skypoint_c VT>
class MccSerializer<VT> : public mcc_serializer_interface_t
{
};
static_assert(mcc_serializer_c<MccSerializer<float>>, "!!!"); static_assert(mcc_serializer_c<MccSerializer<float>>, "!!!");
static_assert(mcc_serializer_c<MccSerializer<impl::MccAngle>>, "!!!"); static_assert(mcc_serializer_c<MccSerializer<impl::MccAngle>>, "!!!");
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SerializedCoordFormat>::is_always_lock_free, "!!!");
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SexagesimalCoordPrec>::is_always_lock_free, "!!!");
static_assert(traits::mcc_formattable<impl::MccAngle>, "!!!");
void f() void f()
{ {
MccSerializer<impl::MccAngle> s; MccSerializer<impl::MccAngle> s;

View File

@@ -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) // https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T> template <typename T>
concept mcc_formattable = concept mcc_formattable = requires(T v) { [](T vv) { return std::format("{}", vv); }(v); };
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); }; // concept mcc_formattable =
// requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types // from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types