This commit is contained in:
2026-06-17 18:31:01 +03:00
parent 6cb54740b4
commit 10703e22d6
4 changed files with 288 additions and 45 deletions

View File

@@ -19,6 +19,12 @@ target_include_directories(
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>" "$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
) )
set(EXAM_STRING str_exam) if(BUILD_EXAMPLES)
add_executable(${EXAM_STRING} examples/str_exam.cpp) set(EXAM_STRING str_exam)
target_link_libraries(${EXAM_STRING} ${PROJECT_NAME}) 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()

100
examples/hmap_example.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include <print>
#include <snipplib/containers/snplib_hmap.h>
using namespace snplib;
int main()
{
int i = 10;
double d = 7.7;
std::string s = "klewjdl";
HeterogenMap<std::string_view> hmap;
bool ok = hmap.push("int_key", i, std::tuple<double, float, long>{});
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<double, float, long, int>{});
if (!ok) {
std::println("cannot insert double value {}", d);
}
auto rd = hmap.get<double>("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<long>("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<std::string_view> hmap_copy(hmap);
auto rs = hmap.get<std::string>("str_key");
if (rs) {
std::println("hmap['str_key'] = {}", rs.value());
} else {
std::println("cannot get hmap['str_key']");
}
rs = hmap_copy.get<std::string>("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<std::string_view> hmap_move(std::move(hmap));
std::println("contains hmap_move['str_key']: {}", hmap_move.contains("str_key"));
rs = hmap_move.get<std::string>("str_key");
if (rs) {
std::println("hmap_move['str_key'] = {}", rs.value());
} else {
std::println("cannot get hmap_move['str_key']");
}
rs = hmap.get<std::string>("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>("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;
}

View File

@@ -3,12 +3,13 @@
#include <concepts> #include <concepts>
#include <ranges> #include <ranges>
namespace snplib { namespace snplib
{
// type T is hashable // type T is hashable
template <typename T> template <typename T>
concept snplib_hashable_c = requires(T t) { concept snplib_hashable_c = requires(T t) {
{ std::hash<T>(t) } -> std::convertible_to<size_t>; { std::hash<T>{}(t) } -> std::convertible_to<size_t>;
}; };
template <typename T, typename CharT = char> template <typename T, typename CharT = char>
@@ -25,4 +26,4 @@ concept snplib_char_view_c = std::ranges::view<T> && std::same_as<std::ranges::r
} // end of snplib namespace } // namespace snplib

View File

@@ -6,7 +6,7 @@
#include <vector> #include <vector>
#include "../concepts/snplib_concepts.h" #include "../concepts/snplib_concepts.h"
#include "../utils/snplib_hash.h" // #include "../utils/snplib_hash.h"
namespace snplib namespace snplib
{ {
@@ -16,30 +16,27 @@ template <snplib_hashable_c KeyT>
class HeterogenMap class HeterogenMap
{ {
protected: protected:
// actuall map key type
typedef std::pair<const HeterogenMap*, KeyT> 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 <typename VT> template <typename VT>
inline static std::unordered_map<key_t, VT, KeyHash> _umap{}; inline static std::unordered_map<const HeterogenMap*, std::unordered_map<KeyT, VT>> _values{};
template <typename UT> template <typename UT>
inline static std::unordered_map<key_t, std::function<UT()>, KeyHash> _getter{}; inline static std::unordered_map<const HeterogenMap*,
std::unordered_map<KeyT, std::function<UT(const HeterogenMap*)>>>
_getter{};
template <typename UT> template <typename UT>
inline static std::unordered_map<key_t, std::function<void(UT const&)>, KeyHash> _setter{}; inline static std::unordered_map<const HeterogenMap*,
std::unordered_map<KeyT, std::function<void(UT const&, const HeterogenMap*)>>>
_setter{};
std::vector<std::function<void()>> _clearFunc{};
// move: void(from_obj_ptr, to_obj_ptr)
inline static std::vector<std::function<void(HeterogenMap*, HeterogenMap*)>> _moveFunc{};
// copy: void(const from_obj_ptr, to_obj_ptr)
inline static std::vector<std::function<void(const HeterogenMap*, HeterogenMap*)>> _copyFunc{};
inline static std::vector<std::function<void(HeterogenMap*)>> _clearFunc{};
inline static std::vector<std::function<bool(KeyT const&, const HeterogenMap*)>> _containsFunc{};
public: public:
enum Error { ERROR_OK, ERROR_NO_ELEM }; enum Error { ERROR_OK, ERROR_NO_ELEM };
@@ -47,42 +44,168 @@ public:
HeterogenMap() = default; HeterogenMap() = default;
~HeterogenMap() ~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) { for (auto& func : _clearFunc) {
func(); func(this);
} }
} }
template <typename VT> template <typename VT>
size_t push(KeyT const& key, VT&& value) bool push(KeyT const& key, VT&& value)
{ {
auto kk = std::make_pair(this, key); using v_t = std::decay_t<VT>;
auto it = _umap<std::decay_t<VT>>.emplace(kk, std::forward<VT>(value));
if (it.second) { // check if the map already contains an element (of any type)
_getter<std::decay_t<VT>>.emplace(kk, [it = it.first]() { return it->second; }); // with the same key
if (contains(key)) {
return false;
}
_clearFunc.emplace_back([it = it.first]() { _umap<std::decay_t<VT>>.erase(it); }); // add before inserting a value
} // here the element already exists (just update its value) if (_values<v_t>.empty()) {
_moveFunc.emplace_back([](HeterogenMap* from_obj, HeterogenMap* to_obj) {
_values<v_t>[to_obj] = std::move(_values<v_t>[from_obj]);
return _clearFunc.size(); _getter<v_t>[to_obj] = std::move(_getter<v_t>[from_obj]);
_setter<v_t>[to_obj] = std::move(_setter<v_t>[from_obj]);
});
_copyFunc.emplace_back([](const HeterogenMap* from_obj, HeterogenMap* to_obj) {
_values<v_t>[to_obj] = _values<v_t>[from_obj];
_getter<v_t>[to_obj] = _getter<v_t>[from_obj];
_setter<v_t>[to_obj] = _setter<v_t>[from_obj];
});
_containsFunc.emplace_back(
[](KeyT const& k, const HeterogenMap* obj) { return _values<v_t>[obj].contains(k); });
_clearFunc.emplace_back([](HeterogenMap* obj) {
_values<v_t>[obj].clear();
_getter<v_t>[obj].clear();
_setter<v_t>[obj].clear();
});
}
_values<v_t>[this].emplace(key, std::forward<VT>(value));
// NOTE: do not use here iterator since after insertion
// an rehashing may occur and all iterators will be invalidated!
_getter<v_t>[this].emplace(key, [key](const HeterogenMap* obj) { return _values<v_t>[obj][key]; });
_setter<v_t>[this].emplace(key, [key](const v_t& v, const HeterogenMap* obj) { _values<v_t>[obj][key] = v; });
return true;
} }
template <typename VT, typename... Ts> template <typename VT, typename... Ts>
size_t push(KeyT const& key, VT&& value, std::tuple<Ts...>) auto push(KeyT const& key, VT&& value, std::tuple<Ts...>)
{ {
auto kk = std::make_pair(this, key); using v_t = std::decay_t<VT>;
auto add_cnv_func = [kk]<size_t I>() { bool ok = push(key, std::forward<VT>(value));
if (!ok) { // element with given 'key' is already in the map
return ok;
}
// trivial conversion functions
auto add_cnv_func = [this]<size_t I>(KeyT const& kk) {
using u_t = std::tuple_element_t<I, std::tuple<Ts...>>; using u_t = std::tuple_element_t<I, std::tuple<Ts...>>;
if constexpr (!std::same_as<VT, u_t>) { if constexpr (!std::same_as<VT, u_t>) { // user type must differ from inserted one
// _umap<u_t>.emplace(kk, ) if constexpr (!requires(v_t v, u_t u) {
v = u;
u = v;
}) {
static_assert(false, "Invalid user type!");
}
_getter<u_t>[this].emplace(kk, [kk](const HeterogenMap* obj) -> u_t { return _values<v_t>[obj][kk]; });
_setter<u_t>[this].emplace(kk,
[kk](const u_t& v, const HeterogenMap* obj) { _values<v_t>[obj][kk] = v; });
_clearFunc.emplace_back([](HeterogenMap* obj) {
_getter<u_t>[obj].clear();
_setter<u_t>[obj].clear();
});
} }
}; };
[kk]<size_t... Is>(std::index_sequence<Is...>) {}(std::make_index_sequence<sizeof...(Ts)>()); [&add_cnv_func, key]<size_t... Is>(std::index_sequence<Is...>) {
(add_cnv_func.template operator()<Is>(key), ...);
}(std::make_index_sequence<sizeof...(Ts)>());
return push(key, std::forward<VT>(value)); return ok;
} }
template <typename VT, typename... CtorArgTs> template <typename VT, typename... CtorArgTs>
@@ -91,15 +214,28 @@ public:
return push(key, VT(std::forward<CtorArgTs>(args)...)); return push(key, VT(std::forward<CtorArgTs>(args)...));
} }
template <typename VT> template <typename UT>
std::expected<VT, Error> get(KeyT const& key) const std::expected<UT, Error> get(KeyT const& key) const
{ {
if (auto it = _umap<VT>.find(std::make_pair(this, key)); it != _umap<VT>.end()) { if (auto it = _getter<UT>[this].find(key); it != _getter<UT>[this].end()) {
return it->second; return (it->second)(this);
} }
return std::unexpected(Error::ERROR_NO_ELEM); return std::unexpected(Error::ERROR_NO_ELEM);
} }
template <typename UT>
Error set(KeyT const& key, UT&& value)
{
using u_t = std::decay_t<UT>;
if (auto it = _setter<u_t>[this].find(key); it != _setter<u_t>[this].end()) {
(it->second)(value, this);
return Error::ERROR_OK;
}
return Error::ERROR_NO_ELEM;
}
}; };
} // end of namespace snplib } // end of namespace snplib