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(); auto it = r.begin();
for (auto const& el : r_str) { 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) { if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
} }
if (it == r.end()) { if (it == r.end()) {
std::back_inserter(r) = val; if constexpr (!traits::mcc_fixed_size_range<R>) {
it = r.end(); std::back_inserter(r) = val;
it = r.end();
}
} else { } else {
*it = val; *it = val;
++it; ++it;

View File

@@ -62,6 +62,26 @@ public:
fromTimePoint(std::forward<decltype(other)>(other).UTC()); 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) MccCelestialCoordEpoch& operator=(mcc_coord_epoch_c auto&& other)
{ {
fromTimePoint(std::forward<decltype(other)>(other).UTC()); fromTimePoint(std::forward<decltype(other)>(other).UTC());
@@ -89,7 +109,7 @@ public:
template <typename VT> template <typename VT>
MccCelestialCoordEpoch& operator=(VT&& mjd) MccCelestialCoordEpoch& operator=(VT&& mjd)
requires std::is_arithmetic_v<VT> requires std::is_arithmetic_v<std::remove_cvref_t<VT>>
{ {
// ignore possible errors!!! // ignore possible errors!!!
auto ok = fromMJD(std::forward<decltype(mjd)>(mjd)); auto ok = fromMJD(std::forward<decltype(mjd)>(mjd));

View File

@@ -176,36 +176,54 @@ public:
template <typename T> template <typename T>
std::expected<T, std::error_code> getValue(std::string_view key) const std::expected<T, std::error_code> getValue(std::string_view key) const
{ {
// T v; // ugly, but works for non-default constructible values
// auto err = forKey(key, [&v](auto const& rec) -> std::error_code { alignas(T) unsigned char buff[sizeof(T)];
// using VT = decltype(rec.value); auto err = forKey(key, [&buff](auto const& rec) -> std::error_code {
// 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) {
using VT = decltype(rec.value); using VT = decltype(rec.value);
if constexpr (std::constructible_from<T, VT>) { if constexpr (std::constructible_from<T, VT>) {
return T{rec.value}; new (buff) T(rec.value);
} else { } 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> template <std::ranges::contiguous_range R, typename T>
std::error_code setValue(R const& key, const T& value) 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 { auto ec = forHash(hash, [&value](auto& rec) -> std::error_code {
using VT = decltype(rec.value); using VT = decltype(rec.value);
if constexpr (std::assignable_from<VT&, T>) { if constexpr (requires { rec.value = value; }) {
rec.value = value; rec.value = value;
} else if constexpr (std::constructible_from<VT, T>) { } else if constexpr (std::constructible_from<VT, T>) {
rec.value = VT(value); 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 { } else {
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE; return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
} }
@@ -450,67 +498,6 @@ protected:
return MccKeyValueHolderErrorCode::ERROR_INVALID_KEY; 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> 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) 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 = concept mcc_range_of_output_char_range =
std::ranges::range<R> && traits::mcc_output_char_range<std::ranges::range_value_t<R>>; 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) // https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T> template <typename T>

View File

@@ -4,6 +4,59 @@
using namespace mcc::impl; using namespace mcc::impl;
// example of non-default constructible class
struct VT {
VT(int v) : _v(v) {}
void set(int v)
{
_v = v;
}
int v() const
{
return _v;
}
protected:
int _v{};
};
template <>
struct mcc::impl::MccSerializer<VT> : MccSerializerBase {
static constexpr std::string_view serializerName{"MCC-VT-SERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_output_char_range auto& output,
VT const& value,
ParamsT const& params = mcc_serialization_params_t{})
{
return MccSerializer<int>{}(output, value.v(), params);
}
};
template <>
struct mcc::impl::MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-VT-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
int v;
auto err = MccDeserializer<int>{}(input, v, params);
if (!err) {
value.set(v);
}
return err;
}
};
static auto kv_desc = std::make_tuple( static auto kv_desc = std::make_tuple(
mcc_simple_kv_record_t{"bb", MccAngle{11.5_degs}, MccAngle{11.5_degs}, mcc_serialization_params_t{}}, mcc_simple_kv_record_t{"bb", MccAngle{11.5_degs}, MccAngle{11.5_degs}, mcc_serialization_params_t{}},
mcc_simple_kv_record_t{"aaa", std::string("AAA"), std::string("AAA"), mcc_serialization_params_t{}}, mcc_simple_kv_record_t{"aaa", std::string("AAA"), std::string("AAA"), mcc_serialization_params_t{}},
@@ -12,7 +65,9 @@ static auto kv_desc = std::make_tuple(
"ddd", MccAngle{11.5_degs}, MccAngle{11.5_degs}, "ddd", MccAngle{11.5_degs}, MccAngle{11.5_degs},
mcc_serialization_params_t{.angle_format = mcc::MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS}}, mcc_serialization_params_t{.angle_format = mcc::MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS}},
mcc_make_simple_kv_record("eee", 1.5), mcc_make_simple_kv_record("eee", 1.5),
mcc_make_simple_kv_record("arr", std::vector<int>{1, 2, 3, 4, 5, 6, 7})); mcc_make_simple_kv_record("arr", std::vector<int>{1, 2, 3, 4, 5, 6, 7}),
mcc_make_simple_kv_record("vt", VT(77)),
mcc_make_simple_kv_record("sarr", std::array<double, 3>{1, 2, 3}));
@@ -88,6 +143,18 @@ int main()
// return 1; // return 1;
} }
err = kv.setValue("arr", std::array{10, 20, 30});
if (err) {
std::println("ERR = {}", err);
// return 1;
}
err = kv.setValue("sarr", std::vector{10, 20, 30, 40, 50});
if (err) {
std::println("ERR = {}", err);
// return 1;
}
std::println("------------------------------------"); std::println("------------------------------------");