ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
signal.hpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2023 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#ifndef NDN_CXX_UTIL_SIGNAL_SIGNAL_HPP
23#define NDN_CXX_UTIL_SIGNAL_SIGNAL_HPP
24
27
28#include <list>
29
30namespace ndn::signal {
31
32class DummyExtraArg;
33
49template<typename Owner, typename... TArgs>
50class Signal : noncopyable
51{
52public: // API for anyone
53#ifndef BOOST_ASSERT_IS_VOID
54 ~Signal() noexcept
55 {
56 BOOST_ASSERT(!m_isExecuting);
57 }
58#endif
59
63 using Handler = std::function<void(const TArgs&...)>;
64
70 connect(Handler handler);
71
78
79private: // API for owner
82 bool
83 isEmpty() const;
84
92 void
93 operator()(const TArgs&... args);
94
98 void
99 operator()(const TArgs&... args, const DummyExtraArg&);
100
101 // make Owner a friend of Signal<Owner, ...> so that API for owner can be called
102 friend Owner;
103
104private: // internal implementation
105 using Self = Signal<Owner, TArgs...>;
106
109 struct Slot
110 {
113 Handler handler;
114
125 shared_ptr<DisconnectFunction> disconnect;
126 };
127
132 using SlotList = std::list<Slot>;
133 SlotList m_slots;
134
137 bool m_isExecuting = false;
138
142 typename SlotList::iterator m_currentSlot;
143
146 void
147 disconnect(typename SlotList::iterator it);
148};
149
150template<typename Owner, typename... TArgs>
151Connection
153{
154 auto it = m_slots.insert(m_slots.end(), {std::move(handler), nullptr});
155 it->disconnect = make_shared<DisconnectFunction>([=] { disconnect(it); });
156
157 return signal::Connection(it->disconnect);
158}
159
160template<typename Owner, typename... TArgs>
163{
164 auto it = m_slots.insert(m_slots.end(), {nullptr, nullptr});
165 it->disconnect = make_shared<DisconnectFunction>([=] { disconnect(it); });
166 signal::Connection conn(it->disconnect);
167
168 it->handler = [conn, handler = std::move(handler)] (const TArgs&... args) mutable {
169 handler(args...);
170 conn.disconnect();
171 };
172
173 return conn;
174}
175
176template<typename Owner, typename... TArgs>
177void
178Signal<Owner, TArgs...>::disconnect(typename SlotList::iterator it)
179{
180 if (m_isExecuting) {
181 // during signal emission, only the currently executing handler can be disconnected
182 BOOST_ASSERT_MSG(it == m_currentSlot, "cannot disconnect another handler from a handler");
183
184 // this serves to indicate that the current slot needs to be erased from the list
185 // after it finishes executing; we cannot do it here because of bug #2333
186 m_currentSlot = m_slots.end();
187
188 // expire all weak_ptrs, to prevent double disconnections
189 it->disconnect.reset();
190 }
191 else {
192 m_slots.erase(it);
193 }
194}
195
196template<typename Owner, typename... TArgs>
197bool
198Signal<Owner, TArgs...>::isEmpty() const
199{
200 return !m_isExecuting && m_slots.empty();
201}
202
203template<typename Owner, typename... TArgs>
204void
205Signal<Owner, TArgs...>::operator()(const TArgs&... args)
206{
207 BOOST_ASSERT_MSG(!m_isExecuting, "cannot emit signal from a handler");
208
209 if (m_slots.empty()) {
210 return;
211 }
212
213 auto guard = make_scope_exit([this] { m_isExecuting = false; });
214 m_isExecuting = true;
215
216 auto it = m_slots.begin();
217 auto last = std::prev(m_slots.end());
218 bool isLast = false;
219 while (!isLast) {
220 m_currentSlot = it;
221 isLast = it == last;
222
223 m_currentSlot->handler(args...);
224
225 if (m_currentSlot == m_slots.end())
226 it = m_slots.erase(it);
227 else
228 ++it;
229 }
230}
231
232template<typename Owner, typename... TArgs>
233void
234Signal<Owner, TArgs...>::operator()(const TArgs&... args, const DummyExtraArg&)
235{
236 this->operator()(args...);
237}
238
239} // namespace ndn::signal
240
241#endif // NDN_CXX_UTIL_SIGNAL_SIGNAL_HPP
Represents a connection to a signal.
void disconnect()
Disconnects from the signal.
(implementation detail) a filler for extra argument
Definition emit.hpp:41
Provides a lightweight signal / event system.
Definition signal.hpp:51
Connection connect(Handler handler)
Connects a handler to the signal.
Definition signal.hpp:152
Connection connectSingleShot(Handler handler)
Connects a single-shot handler to the signal.
Definition signal.hpp:162
~Signal() noexcept
Definition signal.hpp:54
std::function< void(const TArgs &...)> Handler
Represents a function that can connect to the signal.
Definition signal.hpp:63