mcc_keyvalue.h: fix for working with non-default constructible record

values, more smart 'set' operations for range values
mcc_epoch.h: add constructors
mcc_traits.h: add mcc_fixed_size_range and mcc_non_resizable_range
concepts
mcc_deserializer.h: fixes compilation issues for  mcc_fixed_size_range and
mcc_non_resizable_range ranges
This commit is contained in:
2026-05-28 17:39:30 +03:00
parent 3497850e1f
commit e1a136a839
5 changed files with 173 additions and 90 deletions

View File

@@ -132,14 +132,17 @@ protected:
auto it = r.begin();
for (auto const& el : r_str) {
auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
// auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
auto err = dsr(el, val, params);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
if (it == r.end()) {
std::back_inserter(r) = val;
it = r.end();
if constexpr (!traits::mcc_fixed_size_range<R>) {
std::back_inserter(r) = val;
it = r.end();
}
} else {
*it = val;
++it;

View File

@@ -62,6 +62,26 @@ public:
fromTimePoint(std::forward<decltype(other)>(other).UTC());
}
MccCelestialCoordEpoch(traits::mcc_input_char_range auto&& str)
{
// ignore possible errors!!!
fromCharRange(std::forward<decltype(str)>(str));
}
template <typename ClockT, typename DurT>
MccCelestialCoordEpoch(std::chrono::time_point<ClockT, DurT> const& tp) : MccCelestialCoordEpoch()
{
fromTimePoint(tp);
}
template <typename VT>
MccCelestialCoordEpoch(VT&& mjd)
requires std::is_arithmetic_v<std::remove_cvref_t<VT>>
{
// ignore possible errors!!!
fromMJD(std::forward<decltype(mjd)>(mjd));
}
MccCelestialCoordEpoch& operator=(mcc_coord_epoch_c auto&& other)
{
fromTimePoint(std::forward<decltype(other)>(other).UTC());
@@ -89,7 +109,7 @@ public:
template <typename VT>
MccCelestialCoordEpoch& operator=(VT&& mjd)
requires std::is_arithmetic_v<VT>
requires std::is_arithmetic_v<std::remove_cvref_t<VT>>
{
// ignore possible errors!!!
auto ok = fromMJD(std::forward<decltype(mjd)>(mjd));

View File

@@ -176,36 +176,54 @@ public:
template <typename T>
std::expected<T, std::error_code> getValue(std::string_view key) const
{
// T v;
// auto err = forKey(key, [&v](auto const& rec) -> std::error_code {
// using VT = decltype(rec.value);
// if constexpr (std::convertible_to<VT, T>) {
// v = rec.value;
// } else if constexpr (std::constructible_from<T, VT>) {
// v = T(rec.value);
// } else {
// return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
// }
// return MccKeyValueHolderErrorCode::ERROR_OK;
// });
// if (err) {
// return std::unexpected(err);
// } else {
// return v;
// }
return forHash1(utils::FNV1aHash(key), [](auto const& rec) {
// ugly, but works for non-default constructible values
alignas(T) unsigned char buff[sizeof(T)];
auto err = forKey(key, [&buff](auto const& rec) -> std::error_code {
using VT = decltype(rec.value);
if constexpr (std::constructible_from<T, VT>) {
return T{rec.value};
new (buff) T(rec.value);
} else {
return std::unexpected(MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE);
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
}
return MccKeyValueHolderErrorCode::ERROR_OK;
});
if (err) {
return std::unexpected(err);
} else {
auto ptr = reinterpret_cast<T*>(&buff[0]);
auto v = std::move(*ptr);
ptr->~T(); // one needs explicitly call destructor here to avvoid side effects!!!
return v;
}
}
// template <typename T>
// std::expected<T, std::error_code> getValue(std::string_view key) const
// {
// T v;
// auto err = forKey(key, [&v](auto const& rec) -> std::error_code {
// using VT = decltype(rec.value);
// if constexpr (std::convertible_to<VT, T>) {
// v = rec.value;
// } else if constexpr (std::constructible_from<T, VT>) {
// v = T(rec.value);
// } else {
// return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
// }
// return MccKeyValueHolderErrorCode::ERROR_OK;
// });
// if (err) {
// return std::unexpected(err);
// } else {
// return v;
// }
// }
template <std::ranges::contiguous_range R, typename T>
std::error_code setValue(R const& key, const T& value)
{
@@ -219,10 +237,40 @@ public:
auto ec = forHash(hash, [&value](auto& rec) -> std::error_code {
using VT = decltype(rec.value);
if constexpr (std::assignable_from<VT&, T>) {
if constexpr (requires { rec.value = value; }) {
rec.value = value;
} else if constexpr (std::constructible_from<VT, T>) {
rec.value = VT(value);
} else if constexpr (std::ranges::range<VT> && std::ranges::range<T> &&
requires(std::ranges::range_value_t<VT> v, std::ranges::range_value_t<T> t) {
v = t;
}) {
const auto& val = std::is_pointer_v<std::decay_t<T>> ? std::span{value} : value;
size_t N = std::ranges::distance(rec.value.begin(), rec.value.end());
size_t M = std::ranges::distance(val.begin(), val.end());
auto it = std::ranges::begin(rec.value);
std::ranges::for_each_n(val.begin(), N > M ? M : N, [&it](auto const& el) {
*it = el;
++it;
});
if (N >= M) { // resize to match the size of the input range
if constexpr (!traits::mcc_non_resizable_range<VT>) {
rec.value.resize(M);
}
} else { // append if allowed
if constexpr (!traits::mcc_fixed_size_range<VT>) {
std::ranges::for_each_n(val.begin(), N, [&it](auto const& el) {
*it = el;
++it;
});
std::ranges::for_each(val | std::views::drop(N),
[&rec](auto const& el) { std::back_inserter(rec.value) = el; });
}
}
} else {
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
}
@@ -450,67 +498,6 @@ protected:
return MccKeyValueHolderErrorCode::ERROR_INVALID_KEY;
}
template <typename FuncT>
auto forHash1(this auto&& self, size_t hash, FuncT&& func)
{
return std::forward<decltype(self)>(self).template forHash1Impl<NUMBER_OF_RECORDS / 2>(
hash, std::forward<FuncT>(func), 0, NUMBER_OF_RECORDS - 1);
}
//
// implements a binary search
//
template <size_t I = NUMBER_OF_RECORDS / 2, typename FuncT>
auto forHash1Impl(this auto&& self,
size_t hash,
FuncT&& func,
size_t start = 0,
size_t stop = NUMBER_OF_RECORDS - 1)
{
using func_t = std::remove_cvref_t<FuncT>;
if constexpr (I < NUMBER_OF_RECORDS) {
if (hash == std::forward<decltype(self)>(self)._mapHash[I].first) {
constexpr auto idx = std::forward<decltype(self)>(self)._mapHash[I].second;
using rec_t = std::tuple_element_t<idx, DESCR_T>;
if constexpr (std::is_void_v<std::invoke_result_t<func_t, rec_t>>) {
std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
} else {
return std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
}
} else if (hash < std::forward<decltype(self)>(self)._mapHash[I].first) {
if (stop - start > 1) {
return std::forward<decltype(self)>(self).template forHash1Impl<I / 2>(
hash, std::forward<FuncT>(func), start, I - 1);
}
const auto idx = self._mapHash[I - 1].second;
using rec_t = std::tuple_element_t<idx, DESCR_T>;
if constexpr (std::is_void_v<std::invoke_result_t<func_t, rec_t>>) {
std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
} else {
return std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
}
} else { // hash > std::forward<decltype(self)>(self)._mapHash[I].first
if (stop - start > 1) {
return std::forward<decltype(self)>(self).template forHash1Impl<I / 2>(
hash, std::forward<FuncT>(func), I + 1, stop);
}
const auto idx = self._mapHash[I + 1].second;
using rec_t = std::tuple_element_t<idx, DESCR_T>;
if constexpr (std::is_void_v<std::invoke_result_t<func_t, rec_t>>) {
std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
} else {
return std::forward<FuncT>(func)(std::get<idx>(std::forward<decltype(self)>(self)._keyValue));
}
}
}
}
template <size_t I = 0, traits::mcc_output_char_range R, traits::mcc_input_char_range RecDelimT>
std::error_code formatRecord(R& output_buffer, RecDelimT rec_delim, bool is_default = false)
{

View File

@@ -56,7 +56,13 @@ template <typename R>
concept mcc_range_of_output_char_range =
std::ranges::range<R> && traits::mcc_output_char_range<std::ranges::range_value_t<R>>;
// std::array, std::span(v_t, N), std::tuple
template <typename R>
concept mcc_fixed_size_range = std::ranges::range<R> && requires { std::tuple_size<std::remove_cvref_t<R>>::value; };
// non-resizable ranges
template <typename R>
concept mcc_non_resizable_range = std::ranges::range<R> && !requires(R r, std::size_t n) { r.resize(n); };
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T>