From 10703e22d6ab39d7f2c6d6478c73f122fea14919 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Wed, 17 Jun 2026 18:31:01 +0300 Subject: [PATCH] ... --- CMakeLists.txt | 12 +- examples/hmap_example.cpp | 100 +++++++++ include/snipplib/concepts/snplib_concepts.h | 7 +- include/snipplib/containers/snplib_hmap.h | 214 ++++++++++++++++---- 4 files changed, 288 insertions(+), 45 deletions(-) create mode 100644 examples/hmap_example.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bb57d0d..37fc1f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,12 @@ target_include_directories( "$" ) -set(EXAM_STRING str_exam) -add_executable(${EXAM_STRING} examples/str_exam.cpp) -target_link_libraries(${EXAM_STRING} ${PROJECT_NAME}) +if(BUILD_EXAMPLES) + set(EXAM_STRING str_exam) + add_executable(${EXAM_STRING} examples/str_exam.cpp) + target_link_libraries(${EXAM_STRING} ${PROJECT_NAME}) + + set(EXAM_HMAP hmap_example) + add_executable(${EXAM_HMAP} examples/hmap_example.cpp) + target_link_libraries(${EXAM_HMAP} ${PROJECT_NAME}) +endif() diff --git a/examples/hmap_example.cpp b/examples/hmap_example.cpp new file mode 100644 index 0000000..293ae49 --- /dev/null +++ b/examples/hmap_example.cpp @@ -0,0 +1,100 @@ +#include + +#include + + +using namespace snplib; + +int main() +{ + int i = 10; + double d = 7.7; + std::string s = "klewjdl"; + + HeterogenMap hmap; + + bool ok = hmap.push("int_key", i, std::tuple{}); + if (!ok) { + std::println("cannot insert integer value {}", i); + } + + ok = hmap.push("str_key", s); + if (!ok) { + std::println("cannot insert string value {}", s); + } + + ok = hmap.push("dbl_key", d, std::tuple{}); + if (!ok) { + std::println("cannot insert double value {}", d); + } + + auto rd = hmap.get("int_key"); + if (rd) { + std::println("hmap[int_key] = {}", rd.value()); + } else { + std::println("cannot convert to double value with key 'int_key'"); + } + + auto rl = hmap.get("dbl_key"); + if (rl) { + std::println("hmap[dbl_key] = {}", rl.value()); + } else { + std::println("cannot convert to long value with key 'dbl_key'"); + } + + std::println("\n{:*^80}", " copy ctor "); + + HeterogenMap hmap_copy(hmap); + + auto rs = hmap.get("str_key"); + if (rs) { + std::println("hmap['str_key'] = {}", rs.value()); + } else { + std::println("cannot get hmap['str_key']"); + } + + rs = hmap_copy.get("str_key"); + if (rs) { + std::println("hmap_copy['str_key'] = {}", rs.value()); + } else { + std::println("cannot get hmap_copy['str_key']"); + } + + std::println("\n{:*^80}", " move ctor "); + + HeterogenMap hmap_move(std::move(hmap)); + std::println("contains hmap_move['str_key']: {}", hmap_move.contains("str_key")); + + rs = hmap_move.get("str_key"); + if (rs) { + std::println("hmap_move['str_key'] = {}", rs.value()); + } else { + std::println("cannot get hmap_move['str_key']"); + } + + rs = hmap.get("str_key"); + if (rs) { + std::println("hmap['str_key'] = {}", rs.value()); + } else { + std::println("cannot get hmap['str_key']"); + std::println("contains hmap['str_key']: {}", hmap.contains("str_key")); + } + + + std::println("\n{:*^80}", " set value "); + + double dd = 123.456789; + std::println("set hmap_copy['int_key'] to {} ...", dd); + if (hmap_copy.set("int_key", dd)) { + std::println("cannot set hmap_copy['int_key'] to {}", dd); + } else { + auto ri = hmap_copy.get("int_key"); + if (ri) { + std::println("hmap_copy['int_key'] = {}", ri.value()); + } else { + std::println("cannot get value of hmap_copy['int_key']"); + } + } + + return 0; +} \ No newline at end of file diff --git a/include/snipplib/concepts/snplib_concepts.h b/include/snipplib/concepts/snplib_concepts.h index aae0eab..e66af12 100644 --- a/include/snipplib/concepts/snplib_concepts.h +++ b/include/snipplib/concepts/snplib_concepts.h @@ -3,12 +3,13 @@ #include #include -namespace snplib { +namespace snplib +{ // type T is hashable template concept snplib_hashable_c = requires(T t) { - { std::hash(t) } -> std::convertible_to; + { std::hash{}(t) } -> std::convertible_to; }; template @@ -25,4 +26,4 @@ concept snplib_char_view_c = std::ranges::view && std::same_as #include "../concepts/snplib_concepts.h" -#include "../utils/snplib_hash.h" +// #include "../utils/snplib_hash.h" namespace snplib { @@ -16,30 +16,27 @@ template class HeterogenMap { protected: - // actuall map key type - typedef std::pair key_t; - - struct KeyHash { - static size_t operator()(key_t key) - { - size_t hash = 0; - - snplib_hash_combine(hash, key.first, key.second); - - return hash; - } - }; - template - inline static std::unordered_map _umap{}; + inline static std::unordered_map> _values{}; template - inline static std::unordered_map, KeyHash> _getter{}; + inline static std::unordered_map>> + _getter{}; template - inline static std::unordered_map, KeyHash> _setter{}; + inline static std::unordered_map>> + _setter{}; - std::vector> _clearFunc{}; + + // 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> _containsFunc{}; public: enum Error { ERROR_OK, ERROR_NO_ELEM }; @@ -47,42 +44,168 @@ public: HeterogenMap() = default; ~HeterogenMap() + { + clear(); + } + + + HeterogenMap(const HeterogenMap& other) + { + if (this == &other) { + return; + } + + for (auto& func : _copyFunc) { + func(&other, this); + } + } + + HeterogenMap(HeterogenMap&& other) + { + if (this == &other) { + return; + } + + for (auto& func : _moveFunc) { + func(&other, this); + } + } + + HeterogenMap& operator=(const HeterogenMap& other) + { + if (this == &other) { + return *this; + } + + for (auto& func : _copyFunc) { + func(&other, this); + } + + return *this; + } + + HeterogenMap& operator=(HeterogenMap&& 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(); + func(this); } } template - size_t push(KeyT const& key, VT&& value) + bool push(KeyT const& key, VT&& value) { - auto kk = std::make_pair(this, key); - auto it = _umap>.emplace(kk, std::forward(value)); + using v_t = std::decay_t; - if (it.second) { - _getter>.emplace(kk, [it = it.first]() { return it->second; }); + // check if the map already contains an element (of any type) + // with the same key + if (contains(key)) { + return false; + } - _clearFunc.emplace_back([it = it.first]() { _umap>.erase(it); }); - } // here the element already exists (just update its value) + // add before inserting a value + if (_values.empty()) { + _moveFunc.emplace_back([](HeterogenMap* from_obj, HeterogenMap* to_obj) { + _values[to_obj] = std::move(_values[from_obj]); - return _clearFunc.size(); + _getter[to_obj] = std::move(_getter[from_obj]); + _setter[to_obj] = std::move(_setter[from_obj]); + }); + + _copyFunc.emplace_back([](const HeterogenMap* from_obj, HeterogenMap* 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 HeterogenMap* obj) { return _values[obj].contains(k); }); + + _clearFunc.emplace_back([](HeterogenMap* obj) { + _values[obj].clear(); + + _getter[obj].clear(); + _setter[obj].clear(); + }); + } + + _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 HeterogenMap* obj) { return _values[obj][key]; }); + _setter[this].emplace(key, [key](const v_t& v, const HeterogenMap* obj) { _values[obj][key] = v; }); + + + return true; } template - size_t push(KeyT const& key, VT&& value, std::tuple) + auto push(KeyT const& key, VT&& value, std::tuple) { - auto kk = std::make_pair(this, key); + using v_t = std::decay_t; - auto add_cnv_func = [kk]() { + bool ok = push(key, std::forward(value)); + if (!ok) { // element with given 'key' is already in the map + return ok; + } + + + // trivial conversion functions + auto add_cnv_func = [this](KeyT const& kk) { using u_t = std::tuple_element_t>; - if constexpr (!std::same_as) { - // _umap.emplace(kk, ) + if constexpr (!std::same_as) { // user type must differ from inserted one + if constexpr (!requires(v_t v, u_t u) { + v = u; + u = v; + }) { + static_assert(false, "Invalid user type!"); + } + + _getter[this].emplace(kk, [kk](const HeterogenMap* obj) -> u_t { return _values[obj][kk]; }); + _setter[this].emplace(kk, + [kk](const u_t& v, const HeterogenMap* obj) { _values[obj][kk] = v; }); + + _clearFunc.emplace_back([](HeterogenMap* obj) { + _getter[obj].clear(); + _setter[obj].clear(); + }); } }; - [kk](std::index_sequence) {}(std::make_index_sequence()); + [&add_cnv_func, key](std::index_sequence) { + (add_cnv_func.template operator()(key), ...); + }(std::make_index_sequence()); - return push(key, std::forward(value)); + return ok; } template @@ -91,15 +214,28 @@ public: return push(key, VT(std::forward(args)...)); } - template - std::expected get(KeyT const& key) const + template + std::expected get(KeyT const& key) const { - if (auto it = _umap.find(std::make_pair(this, key)); it != _umap.end()) { - return it->second; + if (auto it = _getter[this].find(key); it != _getter[this].end()) { + return (it->second)(this); } return std::unexpected(Error::ERROR_NO_ELEM); } + + template + Error set(KeyT const& key, UT&& value) + { + using u_t = std::decay_t; + + if (auto it = _setter[this].find(key); it != _setter[this].end()) { + (it->second)(value, this); + return Error::ERROR_OK; + } + + return Error::ERROR_NO_ELEM; + } }; } // end of namespace snplib \ No newline at end of file