From d15928eb689d70c70a18c6887a80d836416abd92 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Wed, 2 Sep 2026 16:25:14 +0300 Subject: [PATCH] new implementation: hmap class --- include/snipplib/containers/snplib_hmap.h | 426 +++++++++++++++++++++- 1 file changed, 425 insertions(+), 1 deletion(-) diff --git a/include/snipplib/containers/snplib_hmap.h b/include/snipplib/containers/snplib_hmap.h index 54730bd..2dbd23f 100644 --- a/include/snipplib/containers/snplib_hmap.h +++ b/include/snipplib/containers/snplib_hmap.h @@ -351,4 +351,428 @@ public: } }; -} // end of namespace snplib \ No newline at end of file +} // end of namespace snplib + + + +namespace snplib +{ + +// error codes +enum class hmap_error_e : int { ERROR_OK, ERROR_INVALID_KEY, ERROR_USER_CONV }; + +} // namespace snplib + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + +namespace snplib +{ + +struct hmap_error_category_t : std::error_category { + const char* name() const noexcept + { + return "SNPLIB-HMAP-ERR-CATEGORY"; + } + + std::string message(int ec) const + { + hmap_error_e err = static_cast(ec); + + switch (err) { + case hmap_error_e::ERROR_OK: + return "OK"; + case hmap_error_e::ERROR_INVALID_KEY: + return "invalid key"; + case hmap_error_e::ERROR_USER_CONV: + return "an error in user conversion function"; + default: + return "UNKNOWN"; + } + } + + static const hmap_error_category_t& get() + { + static const hmap_error_category_t constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(hmap_error_e ec) +{ + return std::error_code(static_cast(ec), hmap_error_category_t::get()); +} + + +template +class hmap +{ +public: + typedef std::error_code error_t; + + template + using hmap_expected_t = std::expected; + + template + using hmap_user_conv_from_t = std::function(hmap_expected_t const&)>; + + template + using hmap_user_conv_to_t = std::function(hmap_expected_t const&)>; + +protected: + template + inline static std::unordered_map> _values{}; + + template + inline static std::unordered_map(const hmap*)>>> + _getter{}; + + template + inline static std::unordered_map>> + _setter{}; + + + // move: void(from_obj_ptr, to_obj_ptr) + inline static std::vector> _moveFunc{}; + // copy: void(const from_obj_ptr, to_obj_ptr) + inline static std::vector> _copyFunc{}; + + inline static std::vector> _clearFunc{}; + inline static std::vector> _sizeFunc{}; + inline static std::vector> _eraseFunc{}; + inline static std::vector> _containsFunc{}; + + + // trivial conversional functors + struct trivial_conv_from_t { + hmap const* obj; + KeyT const& key; + + template + hmap_expected_t operator()(hmap_expected_t const&) + { + auto v = obj->get(key); + if (v) { + return static_cast(v.value()); + } + + return hmap_error_e::ERROR_USER_CONV; + } + }; + + struct trivial_conv_to_t { + hmap const* obj; + KeyT const& key; + + template + hmap_expected_t operator()(hmap_expected_t const& uval) + { + if (uval) { + auto err = obj->set(key, uval.value()); + if (err) { + return std::unexpected(err); + } + + // return + } + + return hmap_error_e::ERROR_USER_CONV; + } + }; + + + template + requires requires(VT v, UT u) { v = u; } + inline static std::function(hmap_expected_t const&)> trivial_conv_from = + [](hmap_expected_t const& val) -> hmap_expected_t { + if (val) { + return static_cast(val.value()); + } else { + return val.error(); + } + }; + + template + requires requires(VT v, UT u) { u = v; } + inline static std::function(hmap_expected_t const&)> trivial_conv_to = + [](hmap_expected_t const& uval) -> hmap_expected_t { + if (uval) { + return static_cast(uval.value()); + } else { + return uval.error(); + } + }; + +public: + hmap() = default; + + virtual ~hmap() + { + clear(); + } + + + hmap(const hmap& other) + { + if (this == &other) { + return; + } + + for (auto& func : _copyFunc) { + func(&other, this); + } + } + + hmap(hmap&& other) + { + if (this == &other) { + return; + } + + for (auto& func : _moveFunc) { + func(&other, this); + } + } + + hmap& operator=(const hmap& other) + { + if (this == &other) { + return *this; + } + + for (auto& func : _copyFunc) { + func(&other, this); + } + + return *this; + } + + hmap& operator=(hmap&& other) + { + if (this == &other) { + return *this; + } + + for (auto& func : _moveFunc) { + func(&other, this); + } + + return *this; + } + + bool contains(KeyT const& key) const + { + bool does_contain = false; + + for (auto& func : _containsFunc) { + if (func(key, this)) { + does_contain = true; + break; + } + } + + return does_contain; + } + + void clear() + { + for (auto& func : _clearFunc) { + func(this); + } + } + + + size_t size() const + { + size_t N = 0; + + for (auto& func : _sizeFunc) { + N += func(this); + } + + return N; + } + + template + bool push(KeyT const& key, VT&& value) + { + using v_t = std::decay_t; + + // check if the map already contains an element (of any type) + // with the same key + if (contains(key)) { + return false; + } + + // add before inserting a value + if (_values.empty()) { + _moveFunc.emplace_back([](hmap* from_obj, hmap* to_obj) { + _values[to_obj] = std::move(_values[from_obj]); + + _getter[to_obj] = std::move(_getter[from_obj]); + _setter[to_obj] = std::move(_setter[from_obj]); + }); + + _copyFunc.emplace_back([](const hmap* from_obj, hmap* to_obj) { + _values[to_obj] = _values[from_obj]; + + _getter[to_obj] = _getter[from_obj]; + _setter[to_obj] = _setter[from_obj]; + }); + + _containsFunc.emplace_back([](KeyT const& k, const hmap* obj) { return _values[obj].contains(k); }); + + _clearFunc.emplace_back([](hmap* obj) { + _values.erase(obj); + + _getter.erase(obj); + _setter.erase(obj); + // _values[obj].clear(); + + // _getter[obj].clear(); + // _setter[obj].clear(); + }); + + _sizeFunc.emplace_back([](const hmap* obj) { return _values[obj].size(); }); + + _eraseFunc.emplace_back([](KeyT const& k, const hmap* obj) { + bool do_delete = _values[obj].erase(k) != 0; + if (do_delete) { + _getter[obj].erase(k); + _setter[obj].erase(k); + } + + return do_delete; + }); + } + + _values[this].emplace(key, std::forward(value)); + + // NOTE: do not use here iterator since after insertion + // an rehashing may occur and all iterators will be invalidated! + + _getter[this].emplace(key, [key](const hmap* obj) { return _values[obj][key]; }); + _setter[this].emplace(key, [key](const v_t& v, const hmap* obj) { + _values[obj][key] = v; + return hmap_error_e::ERROR_OK; + }); + + return true; + } + + + // FTs - conversional functions: + // convert-from: std::function(hmap_expected_t const&)> (from inner type to user) + // convert-to: std::function(hmap_expected_t const&)> (from user type to inner) + // + template + requires(sizeof...(FTs) > 1) + auto push(KeyT const& key, VT&& value, FTs&&... cnv_funcs) + { + static_assert(sizeof...(FTs) % 2 == 0, + "IT MUST BE EVEN NUMBER OF THE INPUT CALLABLES!"); // must be even number ("convert-from" and + // "convert-to")! + + using v_t = std::decay_t; + + bool ok = push(key, std::forward(value)); + if (!ok) { // element with given 'key' is already in the map + return ok; + } + + auto add_cnv_func = [this](KeyT const& kk, auto&& from_cnv_func, auto&& to_cnv_func) { + using u_t = std::invoke_result_t; + + // user type must differ from inserted one + static_assert(!std::same_as, "INVALID CONVERSIONAL 'FROM-FUNCTION' SIGNATURE!"); + + _getter[this].emplace( + kk, + [kk, from_cnv_func_arg = + std::forward(from_cnv_func)](const hmap* obj) mutable -> u_t { + return std::forward(from_cnv_func_arg)(_values[obj][kk]); + }); + + _setter[this].emplace(kk, [kk, to_cnv_func_arg = std::forward(to_cnv_func)]( + const u_t& v, const hmap* obj) mutable { + auto val = std::forward(to_cnv_func_arg)(v); + if (val) { + _values[obj][kk] = val; + return hmap_error_e::ERROR_OK; + } else { + return val.error(); + } + }); + + _clearFunc.emplace_back([](hmap* obj) { + _getter[obj].clear(); + _setter[obj].clear(); + }); + + _eraseFunc.emplace_back([](KeyT const& k, const hmap* obj) { + _getter[obj].erase(k); + _setter[obj].erase(k); + + return true; + }); + }; + + [... cnv_funcs_cap = std::forward(cnv_funcs), &add_cnv_func, + key](std::index_sequence) mutable { + auto&& tp = std::forward_as_tuple(std::forward(cnv_funcs_cap)...); + (add_cnv_func(key, std::get(tp), std::get(tp)), ...); + }(std::make_index_sequence()); + + return ok; + } + + template + requires(sizeof...(Ts) > 0) + auto push(KeyT const& key, VT&& value, std::tuple) + { + using tp_t = std::tuple; + + return [&key, val = std::forward(value), this](std::index_sequence) { + return std::apply([this](auto&&... args) { return push(std::forward(args)...); }, + std::tuple_cat(std::forward_as_tuple(key, std::forward(val)), + std::tuple_cat(std::forward_as_tuple( + hmap::trivial_conv_from>, + hmap::trivial_conv_to>)...))); + }(std::make_index_sequence{}); + } + + template + hmap_expected_t get(KeyT const& key) const + { + if (auto it = _getter[this].find(key); it != _getter[this].end()) { + return (it->second)(this); + } + + return std::unexpected(hmap_error_e::ERROR_INVALID_KEY); + } + + template + hmap::error_t set(KeyT const& key, UT&& value) + { + using u_t = std::decay_t; + + if (auto it = _setter[this].find(key); it != _setter[this].end()) { + return (it->second)(value, this); + } + + return hmap_error_e::ERROR_INVALID_KEY; + } +}; + +} // namespace snplib