annotate common/signals.h @ 0:1158cffe5a5e

Initial import
author David Demelier <markand@malikania.fr>
date Mon, 08 Feb 2016 16:43:14 +0100
parents
children 03068f5ed79d
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
1 /*
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
2 * signals.h -- synchronous observer mechanism
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
3 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
5 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
6 * Permission to use, copy, modify, and/or distribute this software for any
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
7 * purpose with or without fee is hereby granted, provided that the above
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
8 * copyright notice and this permission notice appear in all copies.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
9 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
17 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
18
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
19 #ifndef _IRCCD_SIGNALS_H_
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
20 #define _IRCCD_SIGNALS_H_
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
21
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
22 #include <functional>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
23 #include <stack>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
24 #include <unordered_map>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
25 #include <vector>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
26
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
27 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
28 * @file Signals.h
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
29 * @brief Similar Qt signal subsystem for irccd
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
30 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
31
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
32 namespace irccd {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
33
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
34 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
35 * @class SignalConnection
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
36 * @brief Stores the reference to the callable
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
37 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
38 * This class can be stored to remove a registered function from a Signal, be
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
39 * careful to not mix connections between different signals as they are just
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
40 * referenced by ids.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
41 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
42 class SignalConnection {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
43 private:
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
44 unsigned m_id;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
45
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
46 public:
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
47 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
48 * Create a signal connection.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
49 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
50 * @param id the id
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
51 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
52 inline SignalConnection(unsigned id) noexcept
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
53 : m_id{id}
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
54 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
55 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
56
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
57 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
58 * Get the reference object.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
59 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
60 * @return the id
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
61 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
62 inline unsigned id() const noexcept
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
63 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
64 return m_id;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
65 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
66 };
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
67
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
68 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
69 * @class Signal
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
70 * @brief Stores and call registered functions
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
71 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
72 * This class is intended to be use as a public field in the desired object.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
73 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
74 * The user just have to call one of connect(), disconnect() or the call
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
75 * operator to use this class.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
76 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
77 * It stores the callable as std::function so type-erasure is complete.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
78 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
79 * The user is responsible of taking care that the object is still alive
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
80 * in case that the function takes a reference to the object.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
81 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
82 template <typename... Args>
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
83 class Signal {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
84 private:
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
85 using Function = std::function<void (Args...)>;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
86 using FunctionMap = std::unordered_map<unsigned, Function>;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
87 using Stack = std::stack<unsigned>;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
88
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
89 FunctionMap m_functions;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
90 Stack m_stack;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
91 unsigned m_max{0};
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
92
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
93 public:
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
94 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
95 * Register a new function to the signal.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
96 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
97 * @param function the function
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
98 * @return the connection in case you want to remove it
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
99 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
100 inline SignalConnection connect(Function function) noexcept
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
101 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
102 unsigned id;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
103
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
104 if (!m_stack.empty()) {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
105 id = m_stack.top();
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
106 m_stack.pop();
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
107 } else {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
108 id = m_max ++;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
109 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
110
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
111 m_functions.emplace(id, std::move(function));
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
112
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
113 return SignalConnection{id};
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
114 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
115
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
116 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
117 * Disconnect a connection.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
118 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
119 * @param connection the connection
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
120 * @warning Be sure that the connection belongs to that signal
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
121 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
122 inline void disconnect(const SignalConnection &connection) noexcept
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
123 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
124 auto value = m_functions.find(connection.id());
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
125
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
126 if (value != m_functions.end()) {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
127 m_functions.erase(connection.id());
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
128 m_stack.push(connection.id());
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
129 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
130 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
131
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
132 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
133 * Remove all registered functions.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
134 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
135 inline void clear()
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
136 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
137 m_functions.clear();
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
138 m_max = 0;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
139
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
140 while (!m_stack.empty())
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
141 m_stack.pop();
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
142 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
143
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
144 /**
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
145 * Call every functions.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
146 *
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
147 * @param args the arguments to pass to the signal
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
148 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
149 void operator()(Args... args) const
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
150 {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
151 /*
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
152 * Make a copy of the ids before iterating because the callbacks may eventually remove or modify
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
153 * the list.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
154 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
155 std::vector<unsigned> ids;
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
156
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
157 for (auto &pair : m_functions)
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
158 ids.push_back(pair.first);
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
159
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
160 /*
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
161 * Now iterate while checking if the next id is still available, however if any new signals were
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
162 * added while iterating, they will not be called immediately.
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
163 */
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
164 for (unsigned i : ids) {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
165 auto it = m_functions.find(i);
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
166
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
167 if (it != m_functions.end()) {
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
168 it->second(args...);
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
169 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
170 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
171 }
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
172 };
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
173
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
174 } // !irccd
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
175
1158cffe5a5e Initial import
David Demelier <markand@malikania.fr>
parents:
diff changeset
176 #endif // !_IRCCD_SIGNALS_H_