new implementation: hmap class

This commit is contained in:
2026-09-02 16:25:14 +03:00
parent 4bd33ca3c5
commit d15928eb68

View File

@@ -351,4 +351,428 @@ public:
}
};
} // end of namespace snplib
} // 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<snplib::hmap_error_e> : 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<hmap_error_e>(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<int>(ec), hmap_error_category_t::get());
}
template <snplib_hashable_c KeyT>
class hmap
{
public:
typedef std::error_code error_t;
template <typename VT>
using hmap_expected_t = std::expected<VT, hmap::error_t>;
template <typename VT, typename UT>
using hmap_user_conv_from_t = std::function<hmap_expected_t<UT>(hmap_expected_t<VT> const&)>;
template <typename VT, typename UT>
using hmap_user_conv_to_t = std::function<hmap_expected_t<VT>(hmap_expected_t<UT> const&)>;
protected:
template <typename VT>
inline static std::unordered_map<const hmap*, std::unordered_map<KeyT, VT>> _values{};
template <typename UT>
inline static std::unordered_map<const hmap*,
std::unordered_map<KeyT, std::function<hmap_expected_t<UT>(const hmap*)>>>
_getter{};
template <typename UT>
inline static std::unordered_map<const hmap*,
std::unordered_map<KeyT, std::function<hmap::error_t(UT const&, const hmap*)>>>
_setter{};
// move: void(from_obj_ptr, to_obj_ptr)
inline static std::vector<std::function<void(hmap*, hmap*)>> _moveFunc{};
// copy: void(const from_obj_ptr, to_obj_ptr)
inline static std::vector<std::function<void(const hmap*, hmap*)>> _copyFunc{};
inline static std::vector<std::function<void(hmap*)>> _clearFunc{};
inline static std::vector<std::function<size_t(const hmap*)>> _sizeFunc{};
inline static std::vector<std::function<bool(KeyT const&, const hmap*)>> _eraseFunc{};
inline static std::vector<std::function<bool(KeyT const&, const hmap*)>> _containsFunc{};
// trivial conversional functors
struct trivial_conv_from_t {
hmap const* obj;
KeyT const& key;
template <typename VT, typename UT>
hmap_expected_t<UT> operator()(hmap_expected_t<VT> const&)
{
auto v = obj->get<VT>(key);
if (v) {
return static_cast<UT>(v.value());
}
return hmap_error_e::ERROR_USER_CONV;
}
};
struct trivial_conv_to_t {
hmap const* obj;
KeyT const& key;
template <typename VT, typename UT>
hmap_expected_t<VT> operator()(hmap_expected_t<UT> 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 <typename VT, typename UT>
requires requires(VT v, UT u) { v = u; }
inline static std::function<hmap_expected_t<UT>(hmap_expected_t<VT> const&)> trivial_conv_from =
[](hmap_expected_t<VT> const& val) -> hmap_expected_t<UT> {
if (val) {
return static_cast<UT>(val.value());
} else {
return val.error();
}
};
template <typename VT, typename UT>
requires requires(VT v, UT u) { u = v; }
inline static std::function<hmap_expected_t<VT>(hmap_expected_t<UT> const&)> trivial_conv_to =
[](hmap_expected_t<UT> const& uval) -> hmap_expected_t<VT> {
if (uval) {
return static_cast<VT>(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 <typename VT>
bool push(KeyT const& key, VT&& value)
{
using v_t = std::decay_t<VT>;
// 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<v_t>.empty()) {
_moveFunc.emplace_back([](hmap* from_obj, hmap* to_obj) {
_values<v_t>[to_obj] = std::move(_values<v_t>[from_obj]);
_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 hmap* from_obj, hmap* 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 hmap* obj) { return _values<v_t>[obj].contains(k); });
_clearFunc.emplace_back([](hmap* obj) {
_values<v_t>.erase(obj);
_getter<v_t>.erase(obj);
_setter<v_t>.erase(obj);
// _values<v_t>[obj].clear();
// _getter<v_t>[obj].clear();
// _setter<v_t>[obj].clear();
});
_sizeFunc.emplace_back([](const hmap* obj) { return _values<v_t>[obj].size(); });
_eraseFunc.emplace_back([](KeyT const& k, const hmap* obj) {
bool do_delete = _values<v_t>[obj].erase(k) != 0;
if (do_delete) {
_getter<v_t>[obj].erase(k);
_setter<v_t>[obj].erase(k);
}
return do_delete;
});
}
_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 hmap* obj) { return _values<v_t>[obj][key]; });
_setter<v_t>[this].emplace(key, [key](const v_t& v, const hmap* obj) {
_values<v_t>[obj][key] = v;
return hmap_error_e::ERROR_OK;
});
return true;
}
// FTs - conversional functions:
// convert-from: std::function<hmap_expected_t<UT>(hmap_expected_t<VT> const&)> (from inner type to user)
// convert-to: std::function<hmap_expected_t<VT>(hmap_expected_t<UT> const&)> (from user type to inner)
//
template <typename VT, typename... FTs>
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<VT>;
bool ok = push(key, std::forward<VT>(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<decltype(from_cnv_func), v_t>;
// user type must differ from inserted one
static_assert(!std::same_as<v_t, u_t>, "INVALID CONVERSIONAL 'FROM-FUNCTION' SIGNATURE!");
_getter<u_t>[this].emplace(
kk,
[kk, from_cnv_func_arg =
std::forward<decltype(from_cnv_func)>(from_cnv_func)](const hmap* obj) mutable -> u_t {
return std::forward<decltype(from_cnv_func_arg)>(from_cnv_func_arg)(_values<v_t>[obj][kk]);
});
_setter<u_t>[this].emplace(kk, [kk, to_cnv_func_arg = std::forward<decltype(to_cnv_func)>(to_cnv_func)](
const u_t& v, const hmap* obj) mutable {
auto val = std::forward<decltype(to_cnv_func_arg)>(to_cnv_func_arg)(v);
if (val) {
_values<v_t>[obj][kk] = val;
return hmap_error_e::ERROR_OK;
} else {
return val.error();
}
});
_clearFunc.emplace_back([](hmap* obj) {
_getter<u_t>[obj].clear();
_setter<u_t>[obj].clear();
});
_eraseFunc.emplace_back([](KeyT const& k, const hmap* obj) {
_getter<u_t>[obj].erase(k);
_setter<u_t>[obj].erase(k);
return true;
});
};
[... cnv_funcs_cap = std::forward<FTs>(cnv_funcs), &add_cnv_func,
key]<size_t... Is>(std::index_sequence<Is...>) mutable {
auto&& tp = std::forward_as_tuple(std::forward<FTs>(cnv_funcs_cap)...);
(add_cnv_func(key, std::get<Is * 2>(tp), std::get<Is * 2 + 1>(tp)), ...);
}(std::make_index_sequence<sizeof...(FTs) / 2>());
return ok;
}
template <typename VT, typename... Ts>
requires(sizeof...(Ts) > 0)
auto push(KeyT const& key, VT&& value, std::tuple<Ts...>)
{
using tp_t = std::tuple<Ts...>;
return [&key, val = std::forward<VT>(value), this]<size_t... Is>(std::index_sequence<Is...>) {
return std::apply([this](auto&&... args) { return push(std::forward<decltype(args)>(args)...); },
std::tuple_cat(std::forward_as_tuple(key, std::forward<VT>(val)),
std::tuple_cat(std::forward_as_tuple(
hmap::trivial_conv_from<VT, std::tuple_element_t<Is, tp_t>>,
hmap::trivial_conv_to<VT, std::tuple_element_t<Is, tp_t>>)...)));
}(std::make_index_sequence<sizeof...(Ts)>{});
}
template <typename UT>
hmap_expected_t<UT> get(KeyT const& key) const
{
if (auto it = _getter<UT>[this].find(key); it != _getter<UT>[this].end()) {
return (it->second)(this);
}
return std::unexpected(hmap_error_e::ERROR_INVALID_KEY);
}
template <typename UT>
hmap::error_t 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()) {
return (it->second)(value, this);
}
return hmap_error_e::ERROR_INVALID_KEY;
}
};
} // namespace snplib