Files
mcc/mcc_serializer.h
Timur A. Fatkhullin a4d6f17114 ...
2026-02-01 00:10:28 +03:00

213 lines
6.7 KiB
C++

#pragma once
#include <atomic>
#include <concepts>
#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 <std::derived_from<mcc_serializer_interface_t> SelfT,
traits::mcc_output_char_range R,
typename ValueT,
typename FmtT>
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 const& value, FmtT fmt)
{
return std::forward<SelfT>(self).operator=(output, value, std::move(fmt));
}
};
template <typename T>
concept mcc_serializer_c = std::derived_from<T, mcc_serializer_interface_t>;
/* fallback template */
template <typename VT>
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 <typename FmtT = std::nullptr_t>
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
{
if constexpr (std::formattable<VT, char>) {
if constexpr (std::is_null_pointer_v<FmtT>) {
std::format_to(std::back_inserter(output), "{}", 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 {};
}
protected:
std::string _delimiter{defaultDelimiter};
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
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:
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 <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<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()
{
MccSerializer<impl::MccAngle> s;
std::string str;
s(str, impl::MccAngleALT{1.1});
}
} // namespace mcc