213 lines
6.7 KiB
C++
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
|