...
This commit is contained in:
165
mcc_serializer.h
165
mcc_serializer.h
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user