...
This commit is contained in:
@@ -19,6 +19,12 @@ target_include_directories(
|
|||||||
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
|
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(BUILD_EXAMPLES)
|
||||||
set(EXAM_STRING str_exam)
|
set(EXAM_STRING str_exam)
|
||||||
add_executable(${EXAM_STRING} examples/str_exam.cpp)
|
add_executable(${EXAM_STRING} examples/str_exam.cpp)
|
||||||
target_link_libraries(${EXAM_STRING} ${PROJECT_NAME})
|
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
100
examples/hmap_example.cpp
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user