1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
#pragma once
#include <any>
#include <unordered_map>
#include <functional>
#include <optional>
#include <typeinfo>
#include "base.hpp"
#include "format.hpp"
namespace cru
{
// A map with String as key and any type as value.
// It also has notification when value with specified key changed.
class AnyMap : public Object
{
public:
using ListenerToken = long;
using Listener = std::function<void(const std::any&)>;
AnyMap() = default;
AnyMap(const AnyMap& other) = delete;
AnyMap(AnyMap&& other) = delete;
AnyMap& operator=(const AnyMap& other) = delete;
AnyMap& operator=(AnyMap&& other) = delete;
~AnyMap() override = default;
// return the value if the value exists and the type of value is T.
// return a null optional if value doesn't exists.
// throw std::runtime_error if type is mismatch.
template <typename T>
std::optional<T> GetOptionalValue(const String& key) const
{
try
{
const auto find_result = map_.find(key);
if (find_result != map_.cend())
{
const auto& value = find_result->second.first;
if (value.has_value())
return std::any_cast<T>(value);
return std::nullopt;
}
return std::nullopt;
}
catch (const std::bad_any_cast&)
{
throw std::runtime_error(Format("Value of key \"{}\" in AnyMap is not of the type {}.", ToUtf8String(key), typeid(T).name()));
}
}
// return the value if the value exists and the type of value is T.
// throw if value doesn't exists. (different from "GetOptionalValue").
// throw std::runtime_error if type is mismatch.
template <typename T>
T GetValue(const String& key) const
{
const auto optional_value = GetOptionalValue<T>(key);
if (optional_value.has_value())
return optional_value.value();
else
throw std::runtime_error(Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key)));
}
// Set the value of key, and trigger all related listeners.
template <typename T>
void SetValue(const String& key, T&& value)
{
auto& p = map_[key];
p.first = std::make_any<T>(std::forward<T>(value));
InvokeListeners(p.second, p.first);
}
// Remove the value of the key.
void ClearValue(const String& key)
{
auto& p = map_[key];
p.first = std::any{};
InvokeListeners(p.second, std::any{});
}
// Add a listener which is called when value of key is changed.
// Return a token used to remove the listener.
ListenerToken RegisterValueChangeListener(const String& key, const Listener& listener);
// Remove a listener by token.
void UnregisterValueChangeListener(ListenerToken token);
private:
void InvokeListeners(std::list<ListenerToken>& listener_list, const std::any& value);
private:
std::unordered_map<String, std::pair<std::any, std::list<ListenerToken>>> map_{};
std::unordered_map<ListenerToken, Listener> listeners_{};
ListenerToken current_listener_token_ = 0;
};
}
|