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