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

@@ -3,12 +3,13 @@
#include <concepts>
#include <ranges>
namespace snplib {
namespace snplib
{
// type T is hashable
template <typename 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>
@@ -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 "../concepts/snplib_concepts.h"
#include "../utils/snplib_hash.h"
// #include "../utils/snplib_hash.h"
namespace snplib
{
@@ -16,30 +16,27 @@ template <snplib_hashable_c KeyT>
class HeterogenMap
{
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>
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>
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>
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:
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 <typename VT>
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<std::decay_t<VT>>.emplace(kk, std::forward<VT>(value));
using v_t = std::decay_t<VT>;
if (it.second) {
_getter<std::decay_t<VT>>.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<std::decay_t<VT>>.erase(it); });
} // here the element already exists (just update its value)
// add before inserting a 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>
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...>>;
if constexpr (!std::same_as<VT, u_t>) {
// _umap<u_t>.emplace(kk, )
if constexpr (!std::same_as<VT, u_t>) { // 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<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>
@@ -91,15 +214,28 @@ public:
return push(key, VT(std::forward<CtorArgTs>(args)...));
}
template <typename VT>
std::expected<VT, Error> get(KeyT const& key) const
template <typename UT>
std::expected<UT, Error> get(KeyT const& key) const
{
if (auto it = _umap<VT>.find(std::make_pair(this, key)); it != _umap<VT>.end()) {
return it->second;
if (auto it = _getter<UT>[this].find(key); it != _getter<UT>[this].end()) {
return (it->second)(this);
}
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