forked from NoAvailableAlias/nano-signal-slot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nano_observer.hpp
140 lines (108 loc) · 3.8 KB
/
nano_observer.hpp
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#pragma once
#include <algorithm>
#include <vector>
#include "nano_function.hpp"
#include "nano_mutex.hpp"
namespace Nano
{
template <typename MT_Policy = ST_Policy>
class Observer : private MT_Policy
{
// Only Nano::Signal is allowed private access
template <typename, typename> friend class Signal;
struct Connection
{
Delegate_Key delegate;
typename MT_Policy::Weak_Ptr observer;
Connection() noexcept = default;
Connection(Delegate_Key const& key) : delegate(key), observer() {}
Connection(Delegate_Key const& key, Observer* obs) : delegate(key), observer(obs->weak_ptr()) {}
};
struct Z_Order
{
inline bool operator()(Delegate_Key const& lhs, Delegate_Key const& rhs) const
{
std::size_t x = lhs[0] ^ rhs[0];
std::size_t y = lhs[1] ^ rhs[1];
auto k = (x < y) && x < (x ^ y);
return lhs[k] < rhs[k];
}
inline bool operator()(Connection const& lhs, Connection const& rhs) const
{
return operator()(lhs.delegate, rhs.delegate);
}
};
std::vector<Connection> connections;
//--------------------------------------------------------------------------
void insert(Delegate_Key const& key, Observer* obs)
{
[[maybe_unused]] auto lock = MT_Policy::lock_guard();
auto begin = std::begin(connections);
auto end = std::end(connections);
connections.emplace(std::upper_bound(begin, end, key, Z_Order()), key, obs);
}
void remove(Delegate_Key const& key) noexcept
{
[[maybe_unused]] auto lock = MT_Policy::lock_guard();
auto begin = std::begin(connections);
auto end = std::end(connections);
auto slots = std::equal_range(begin, end, key, Z_Order());
connections.erase(slots.first, slots.second);
}
//--------------------------------------------------------------------------
template <typename Function, typename... Uref>
void for_each(Uref&&... args)
{
auto lock = MT_Policy::lock_guard();
for (auto const& slot : MT_Policy::copy_or_ref(connections, lock))
{
if (auto observer = MT_Policy::observed(slot.observer))
{
Function::bind(slot.delegate)(args...);
}
}
}
template <typename Function, typename Accumulate, typename... Uref>
void for_each_accumulate(Accumulate&& accumulate, Uref&&... args)
{
auto lock = MT_Policy::lock_guard();
for (auto const& slot : MT_Policy::copy_or_ref(connections, lock))
{
if (auto observer = MT_Policy::observed(slot.observer))
{
accumulate(Function::bind(slot.delegate)(args...));
}
}
}
//--------------------------------------------------------------------------
public:
void disconnect_all() noexcept
{
[[maybe_unused]] auto lock = MT_Policy::lock_guard();
for (auto const& slot : connections)
{
if (auto observer = MT_Policy::visiting(slot.observer))
{
static_cast<Observer*>(MT_Policy::unmask(observer))->remove(slot.delegate);
}
}
connections.clear();
}
bool is_empty() const noexcept
{
auto lock = MT_Policy::lock_guard();
return connections.empty();
}
protected:
// Guideline #4: A base class destructor should be
// either public and virtual, or protected and non-virtual.
~Observer()
{
MT_Policy::before_disconnect_all();
disconnect_all();
}
Observer() noexcept = default;
Observer(Observer const&) = delete;
Observer& operator= (Observer const&) = delete;
};
} // namespace Nano ------------------------------------------------------------