...
This commit is contained in:
165
mcc_serializer.h
165
mcc_serializer.h
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <concepts>
|
||||
|
||||
#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 <std::derived_from<mcc_serializer_interface_t> SelfT,
|
||||
traits::mcc_output_char_range R,
|
||||
typename ValueT,
|
||||
typename FmtT = std::format_string<ValueT>>
|
||||
requires(std::same_as<FmtT, std::format_string<ValueT>> || std::same_as<FmtT, std::string_view>)
|
||||
mcc_error_c auto operator()(this SelfT&& self, R& output, ValueT&& value, FmtT fmt = "{}")
|
||||
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, 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>
|
||||
class MccSerializer;
|
||||
|
||||
/* fallback template */
|
||||
template <typename VT>
|
||||
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 <typename FmtT = std::format_string<VT>>
|
||||
error_t operator()(traits::mcc_output_char_range auto& output, VT&& value, FmtT fmt = "{}")
|
||||
requires std::formattable<VT, char>
|
||||
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::same_as<FmtT, std::format_string<VT>>) {
|
||||
std::format_to(std::back_inserter(output), fmt, value);
|
||||
} else if constexpr (std::same_as<FmtT, std::string_view>) {
|
||||
std::vformat_to(std::back_inserter(output), fmt, std::make_format_args(value));
|
||||
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 {};
|
||||
@@ -61,23 +94,113 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
template <mcc_angle_c T>
|
||||
class MccSerializer<T> : public mcc_serializer_interface_t
|
||||
/* 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:
|
||||
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 <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;
|
||||
|
||||
@@ -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 <typename T>
|
||||
concept mcc_formattable =
|
||||
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().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<std::remove_cvref_t<T>>().format(v, ctx); };
|
||||
|
||||
|
||||
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
|
||||
|
||||
Reference in New Issue
Block a user