-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathSimpleSignal.h
223 lines (197 loc) · 7.65 KB
/
SimpleSignal.h
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0/
#pragma once
#include <memory>
#include <functional>
#include <list>
#include <vector>
#include <algorithm>
namespace Simple {
namespace Lib {
/// ProtoSignal is the template implementation for callback list.
template<typename,typename> class ProtoSignal; // undefined
/// CollectorInvocation invokes signal handlers differently depending on return type.
template<typename,typename> struct CollectorInvocation;
/// CollectorLast returns the result of the last signal handler from a signal emission.
template<typename Result>
struct CollectorLast {
using CollectorResult = Result;
explicit CollectorLast () : last_() {}
inline bool operator() (Result r) { last_ = r; return true; }
CollectorResult result () { return last_; }
private:
Result last_;
};
/// CollectorDefault implements the default signal handler collection behaviour.
template<typename Result>
struct CollectorDefault : CollectorLast<Result>
{};
/// CollectorDefault specialisation for signals with void return type.
template<>
struct CollectorDefault<void> {
using CollectorResult = void;
void result () {}
inline bool operator() (void) { return true; }
};
/// CollectorInvocation specialisation for regular signals.
template<class Collector, class R, class... Args>
struct CollectorInvocation<Collector, R (Args...)> {
inline bool
invoke (Collector &collector, const std::function<R (Args...)> &cbf, Args... args) const
{
return collector (cbf (args...));
}
};
/// CollectorInvocation specialisation for signals with void return type.
template<class Collector, class... Args>
struct CollectorInvocation<Collector, void (Args...)> {
inline bool
invoke (Collector &collector, const std::function<void (Args...)> &cbf, Args... args) const
{
cbf (args...); return collector();
}
};
/// ProtoSignal template specialised for the callback signature and collector.
template<class Collector, class R, class... Args>
class ProtoSignal<R (Args...), Collector> : private CollectorInvocation<Collector, R (Args...)> {
protected:
using CbFunction = std::function<R (Args...)>;
using Result = typename CbFunction::result_type;
using CollectorResult = typename Collector::CollectorResult;
private:
/*copy-ctor*/ ProtoSignal (const ProtoSignal&) = delete;
ProtoSignal& operator= (const ProtoSignal&) = delete;
using CallbackSlot = std::shared_ptr<CbFunction>;
using CallbackList = std::list<CallbackSlot>;
CallbackList callback_list_;
size_t add_cb(const CbFunction& cb)
{
callback_list_.emplace_back(std::make_shared<CbFunction>(cb));
return size_t (callback_list_.back().get());
}
bool remove_cb(size_t id)
{
auto it =std::remove_if(begin(callback_list_), end(callback_list_),
[id](const CallbackSlot& slot) { return size_t(slot.get()) == id; });
bool const removed = it != end(callback_list_);
callback_list_.erase(it, end(callback_list_));
return removed;
}
public:
/// ProtoSignal constructor, connects default callback if non-nullptr.
ProtoSignal (const CbFunction &method)
{
if (method)
add_cb(method);
}
/// ProtoSignal destructor releases all resources associated with this signal.
~ProtoSignal ()
{
}
/// Operator to add a new function or lambda as signal handler, returns a handler connection ID.
size_t connect (const CbFunction &cb) { return add_cb(cb); }
/// Operator to remove a signal handler through it connection ID, returns if a handler was removed.
bool disconnect (size_t connection) { return remove_cb(connection); }
/// Emit a signal, i.e. invoke all its callbacks and collect return types with the Collector.
CollectorResult
emit (Args... args) const
{
Collector collector;
for (auto &slot : callback_list_) {
if (slot) {
const bool continue_emission = this->invoke (collector, *slot, args...);
if (!continue_emission)
break;
}
}
return collector.result();
}
// Number of connected slots.
std::size_t
size () const
{
return callback_list_.size();
}
};
} // Lib
// namespace Simple
/**
* Signal is a template type providing an interface for arbitrary callback lists.
* A signal type needs to be declared with the function signature of its callbacks,
* and optionally a return result collector class type.
* Signal callbacks can be added with operator+= to a signal and removed with operator-=, using
* a callback connection ID return by operator+= as argument.
* The callbacks of a signal are invoked with the emit() method and arguments according to the signature.
* The result returned by emit() depends on the signal collector class. By default, the result of
* the last callback is returned from emit(). Collectors can be implemented to accumulate callback
* results or to halt a running emissions in correspondance to callback results.
* The signal implementation is safe against recursion, so callbacks may be removed and
* added during a signal emission and recursive emit() calls are also safe.
* The overhead of an unused signal is intentionally kept very low, around the size of a single pointer.
* Note that the Signal template types is non-copyable.
*/
template <typename SignalSignature, class Collector = Lib::CollectorDefault<typename std::function<SignalSignature>::result_type> >
struct Signal /*final*/ :
Lib::ProtoSignal<SignalSignature, Collector>
{
using ProtoSignal = Lib::ProtoSignal<SignalSignature, Collector>;
using CbFunction = typename ProtoSignal::CbFunction;
/// Signal constructor, supports a default callback as argument.
Signal (const CbFunction &method = CbFunction()) : ProtoSignal (method) {}
};
/// This function creates a std::function by binding @a object to the member function pointer @a method.
template<class Instance, class Class, class R, class... Args> std::function<R (Args...)>
slot (Instance &object, R (Class::*method) (Args...))
{
return [&object, method] (Args... args) { return (object .* method) (args...); };
}
/// This function creates a std::function by binding @a object to the member function pointer @a method.
template<class Class, class R, class... Args> std::function<R (Args...)>
slot (Class *object, R (Class::*method) (Args...))
{
return [object, method] (Args... args) { return (object ->* method) (args...); };
}
/// Keep signal emissions going while all handlers return !0 (true).
template<typename Result>
struct CollectorUntil0 {
using CollectorResult = Result;
explicit CollectorUntil0 () : result_() {}
const CollectorResult& result () { return result_; }
inline bool
operator() (Result r)
{
result_ = r;
return result_ ? true : false;
}
private:
CollectorResult result_;
};
/// Keep signal emissions going while all handlers return 0 (false).
template<typename Result>
struct CollectorWhile0 {
using CollectorResult = Result;
explicit CollectorWhile0 () : result_() {}
const CollectorResult& result () { return result_; }
inline bool
operator() (Result r)
{
result_ = r;
return result_ ? false : true;
}
private:
CollectorResult result_;
};
/// CollectorVector returns the result of the all signal handlers from a signal emission in a std::vector.
template<typename Result>
struct CollectorVector {
using CollectorResult = std::vector<Result>;
const CollectorResult& result () { return result_; }
inline bool
operator() (Result r)
{
result_.push_back (r);
return true;
}
private:
CollectorResult result_;
};
} // Simple