...
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user