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