diff --git a/include/mcc/mcc_deserializer.h b/include/mcc/mcc_deserializer.h index afae187..41bf678 100644 --- a/include/mcc/mcc_deserializer.h +++ b/include/mcc/mcc_deserializer.h @@ -132,14 +132,17 @@ protected: auto it = r.begin(); for (auto const& el : r_str) { - auto err = dsr(el, val, std::forward(params)...); + // auto err = dsr(el, val, std::forward(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) { + std::back_inserter(r) = val; + it = r.end(); + } } else { *it = val; ++it; diff --git a/include/mcc/mcc_epoch.h b/include/mcc/mcc_epoch.h index a71ebae..e98f17f 100644 --- a/include/mcc/mcc_epoch.h +++ b/include/mcc/mcc_epoch.h @@ -62,6 +62,26 @@ public: fromTimePoint(std::forward(other).UTC()); } + MccCelestialCoordEpoch(traits::mcc_input_char_range auto&& str) + { + // ignore possible errors!!! + fromCharRange(std::forward(str)); + } + + template + MccCelestialCoordEpoch(std::chrono::time_point const& tp) : MccCelestialCoordEpoch() + { + fromTimePoint(tp); + } + + template + MccCelestialCoordEpoch(VT&& mjd) + requires std::is_arithmetic_v> + { + // ignore possible errors!!! + fromMJD(std::forward(mjd)); + } + MccCelestialCoordEpoch& operator=(mcc_coord_epoch_c auto&& other) { fromTimePoint(std::forward(other).UTC()); @@ -89,7 +109,7 @@ public: template MccCelestialCoordEpoch& operator=(VT&& mjd) - requires std::is_arithmetic_v + requires std::is_arithmetic_v> { // ignore possible errors!!! auto ok = fromMJD(std::forward(mjd)); diff --git a/include/mcc/mcc_keyvalue.h b/include/mcc/mcc_keyvalue.h index c5a5f38..ca84f12 100644 --- a/include/mcc/mcc_keyvalue.h +++ b/include/mcc/mcc_keyvalue.h @@ -176,36 +176,54 @@ public: template std::expected 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) { - // v = rec.value; - // } else if constexpr (std::constructible_from) { - // 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) { - 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(&buff[0]); + auto v = std::move(*ptr); + ptr->~T(); // one needs explicitly call destructor here to avvoid side effects!!! + + return v; + } } + // template + // std::expected 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) { + // v = rec.value; + // } else if constexpr (std::constructible_from) { + // 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::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) { + if constexpr (requires { rec.value = value; }) { rec.value = value; } else if constexpr (std::constructible_from) { rec.value = VT(value); + } else if constexpr (std::ranges::range && std::ranges::range && + requires(std::ranges::range_value_t v, std::ranges::range_value_t t) { + v = t; + }) { + const auto& val = std::is_pointer_v> ? 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) { + rec.value.resize(M); + } + } else { // append if allowed + if constexpr (!traits::mcc_fixed_size_range) { + 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 - auto forHash1(this auto&& self, size_t hash, FuncT&& func) - { - return std::forward(self).template forHash1Impl( - hash, std::forward(func), 0, NUMBER_OF_RECORDS - 1); - } - - // - // implements a binary search - // - template - 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; - - if constexpr (I < NUMBER_OF_RECORDS) { - if (hash == std::forward(self)._mapHash[I].first) { - constexpr auto idx = std::forward(self)._mapHash[I].second; - using rec_t = std::tuple_element_t; - - if constexpr (std::is_void_v>) { - std::forward(func)(std::get(std::forward(self)._keyValue)); - } else { - return std::forward(func)(std::get(std::forward(self)._keyValue)); - } - } else if (hash < std::forward(self)._mapHash[I].first) { - if (stop - start > 1) { - return std::forward(self).template forHash1Impl( - hash, std::forward(func), start, I - 1); - } - - const auto idx = self._mapHash[I - 1].second; - using rec_t = std::tuple_element_t; - - if constexpr (std::is_void_v>) { - std::forward(func)(std::get(std::forward(self)._keyValue)); - } else { - return std::forward(func)(std::get(std::forward(self)._keyValue)); - } - } else { // hash > std::forward(self)._mapHash[I].first - if (stop - start > 1) { - return std::forward(self).template forHash1Impl( - hash, std::forward(func), I + 1, stop); - } - - const auto idx = self._mapHash[I + 1].second; - using rec_t = std::tuple_element_t; - - if constexpr (std::is_void_v>) { - std::forward(func)(std::get(std::forward(self)._keyValue)); - } else { - return std::forward(func)(std::get(std::forward(self)._keyValue)); - } - } - } - } - template std::error_code formatRecord(R& output_buffer, RecDelimT rec_delim, bool is_default = false) { diff --git a/include/mcc/mcc_traits.h b/include/mcc/mcc_traits.h index 3f02282..11b2ad5 100644 --- a/include/mcc/mcc_traits.h +++ b/include/mcc/mcc_traits.h @@ -56,7 +56,13 @@ template concept mcc_range_of_output_char_range = std::ranges::range && traits::mcc_output_char_range>; +// std::array, std::span(v_t, N), std::tuple +template +concept mcc_fixed_size_range = std::ranges::range && requires { std::tuple_size>::value; }; +// non-resizable ranges +template +concept mcc_non_resizable_range = std::ranges::range && !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 diff --git a/tests/mcc_keyvalue_test.cpp b/tests/mcc_keyvalue_test.cpp index 063dc1c..33e64b0 100644 --- a/tests/mcc_keyvalue_test.cpp +++ b/tests/mcc_keyvalue_test.cpp @@ -4,6 +4,59 @@ 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 : MccSerializerBase { + static constexpr std::string_view serializerName{"MCC-VT-SERIALIZER"}; + + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + return MccSerializer{}(output, value.v(), params); + } +}; + +template <> +struct mcc::impl::MccDeserializer : MccDeserializerBase { + static constexpr std::string_view deserializerName{"MCC-VT-DESERIALIZER"}; + + template + 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{}(input, v, params); + if (!err) { + value.set(v); + } + + return err; + } +}; + + 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{"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}, 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("arr", std::vector{1, 2, 3, 4, 5, 6, 7})); + mcc_make_simple_kv_record("arr", std::vector{1, 2, 3, 4, 5, 6, 7}), + mcc_make_simple_kv_record("vt", VT(77)), + mcc_make_simple_kv_record("sarr", std::array{1, 2, 3})); @@ -88,6 +143,18 @@ int main() // 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("------------------------------------");