17 #include <type_traits>
19 #include <forward_list>
22 static_assert(
false,
"KDBindings is not compatible with Qt's 'emit' keyword.\n"
23 "To use KDBindings with Qt, please define QT_NO_EMIT to disable Qt's 'emit' keyword.\n"
24 "If you're using CMake you can set KDBindings_QT_NO_EMIT to ON to add this.");
70 template<
typename... Args>
74 std::conjunction<std::negation<std::is_rvalue_reference<Args>>...>::value,
75 "R-value references are not allowed as Signal parameters!");
80 class Impl :
public Private::SignalImplBase
88 Impl(Impl
const &other) =
delete;
89 Impl &
operator=(Impl
const &other) =
delete;
92 Impl(Impl &&other) =
delete;
97 Private::GenerationalIndex
connect(std::function<
void(Args...)>
const &slot)
99 Connection newConnection;
100 newConnection.slot = slot;
101 return m_connections.insert(std::move(newConnection));
107 Private::GenerationalIndex
connectDeferred(
const std::shared_ptr<ConnectionEvaluator> &evaluator, std::function<
void(Args...)>
const &slot)
109 auto weakEvaluator = std::weak_ptr<ConnectionEvaluator>(evaluator);
111 auto deferredSlot = [weakEvaluator = std::move(weakEvaluator), slot](
ConnectionHandle &handle, Args... args) {
112 if (
auto evaluatorPtr = weakEvaluator.lock()) {
113 auto lambda = [slot, args...]() {
116 evaluatorPtr->enqueueSlotInvocation(handle, lambda);
118 throw std::runtime_error(
"ConnectionEvaluator is no longer alive");
122 Connection newConnection;
123 newConnection.m_connectionEvaluator = evaluator;
124 newConnection.slotReflective = deferredSlot;
126 return m_connections.insert(std::move(newConnection));
131 Connection newConnection;
132 newConnection.slotReflective = slot;
134 return m_connections.insert(std::move(newConnection));
145 auto idOpt = handle.m_id;
148 if (idOpt.has_value()) {
149 auto id = idOpt.value();
152 auto connection = m_connections.get(
id);
153 if (connection && m_isEmitting) {
156 connection->toBeDisconnected =
true;
157 m_disconnectedDuringEmit =
true;
161 if (connection && connection->m_connectionEvaluator.lock()) {
162 if (
auto evaluatorPtr = connection->m_connectionEvaluator.lock()) {
163 evaluatorPtr->dequeueSlotInvocation(handle);
169 m_connections.erase(
id);
179 const auto numEntries = m_connections.entriesSize();
181 const auto sharedThis = shared_from_this();
182 for (
auto i = decltype(numEntries){ 0 }; i < numEntries; ++i) {
183 const auto indexOpt = m_connections.indexAtEntry(i);
184 if (sharedThis && indexOpt) {
190 bool blockConnection(
const Private::GenerationalIndex &
id,
bool blocked)
override
192 Connection *connection = m_connections.get(
id);
194 const bool wasBlocked = connection->blocked;
195 connection->blocked = blocked;
198 throw std::out_of_range(
"Provided ConnectionHandle does not match any connection\nLikely the connection was deleted before!");
202 bool isConnectionActive(
const Private::GenerationalIndex &
id)
const noexcept
override
204 return m_connections.get(
id);
209 auto connection = m_connections.get(
id);
211 return connection->blocked;
213 throw std::out_of_range(
"Provided ConnectionHandle does not match any connection\nLikely the connection was deleted before!");
220 throw std::runtime_error(
"Signal is already emitting, nested emits are not supported!");
224 const auto numEntries = m_connections.entriesSize();
228 for (
auto i = decltype(numEntries){ 0 }; i < numEntries; ++i) {
229 const auto index = m_connections.indexAtEntry(i);
232 const auto con = m_connections.get(*index);
235 if (con->slotReflective) {
236 if (
auto sharedThis = shared_from_this(); sharedThis) {
238 con->slotReflective(handle, p...);
240 }
else if (con->slot) {
246 m_isEmitting =
false;
248 if (m_disconnectedDuringEmit) {
249 m_disconnectedDuringEmit =
false;
254 for (
auto i = decltype(numEntries){ 0 }; i < numEntries; ++i) {
255 const auto index = m_connections.indexAtEntry(i);
257 if (index.has_value()) {
258 const auto con = m_connections.get(index.value());
259 if (con->toBeDisconnected) {
270 std::function<void(Args...)> slot;
272 std::weak_ptr<ConnectionEvaluator> m_connectionEvaluator;
273 bool blocked{
false };
276 bool toBeDisconnected{
false };
279 mutable Private::GenerationalIndexArray<Connection> m_connections;
292 bool m_isEmitting =
false;
293 bool m_disconnectedDuringEmit =
false;
412 handle.setId(m_impl->connectDeferred(evaluator, slot));
454 template<
typename Func,
typename... FuncArgs,
typename = std::enable_if_t<std::disjunction_v<std::negation<std::is_convertible<Func, std::function<void(Args...)>>>, std::integral_constant<bool,
sizeof...(FuncArgs) >>>>
457 std::function<void(Args...)> bound = Private::bind_first(std::forward<Func>(slot), std::forward<FuncArgs>(args)...);
472 if (m_impl && handle.
belongsTo(*
this) && handle.m_id.has_value()) {
473 m_impl->disconnect(handle);
476 throw std::out_of_range(
"Provided ConnectionHandle does not match any connection\nLikely the connection was deleted before!");
492 m_impl->disconnectAll();
520 if (m_impl && handle.
belongsTo(*
this) && handle.m_id.has_value()) {
521 return m_impl->blockConnection(*handle.m_id, blocked);
523 throw std::out_of_range(
"Provided ConnectionHandle does not match any connection\nLikely the connection was deleted before!");
540 throw std::out_of_range(
"Provided ConnectionHandle does not match any connection\nLikely the connection was deleted before!");
543 if (handle.m_id.has_value()) {
544 return m_impl->isConnectionBlocked(*handle.m_id);
582 m_impl = std::make_shared<Impl>();
598 mutable std::shared_ptr<Impl> m_impl;
624 m_wasBlocked = m_handle.
block(
true);
633 m_handle.block(m_wasBlocked);
638 bool m_wasBlocked{
false };
#define KDBINDINGS_WARN_UNUSED
A ConnectionBlocker is a convenient RAII-style mechanism for temporarily blocking a connection.
ConnectionBlocker(const ConnectionHandle &handle)
A ConnectionHandle represents the connection of a Signal to a slot (i.e. a function that is called wh...
bool belongsTo(const Signal< Args... > &signal) const
A Signal provides a mechanism for communication between objects.
Signal(Signal &&other) noexcept=default
bool isConnectionBlocked(const ConnectionHandle &handle) const
KDBINDINGS_WARN_UNUSED ConnectionHandle connectDeferred(const std::shared_ptr< ConnectionEvaluator > &evaluator, std::function< void(Args...)> const &slot)
Establishes a deferred connection between the provided evaluator and slot.
void disconnect(const ConnectionHandle &handle)
bool blockConnection(const ConnectionHandle &handle, bool blocked)
Signal & operator=(Signal &&other) noexcept=default
void disconnectAll() noexcept
KDBINDINGS_WARN_UNUSED ConnectionHandle connectReflective(std::function< void(ConnectionHandle &, Args...)> const &slot)
void emit(Args... p) const
Signal(const Signal &)=delete
KDBINDINGS_WARN_UNUSED ConnectionHandle connect(Func &&slot, FuncArgs &&...args)
KDBINDINGS_WARN_UNUSED ConnectionHandle connect(std::function< void(Args...)> const &slot)
Signal & operator=(Signal const &other)=delete
KDBINDINGS_WARN_UNUSED ConnectionHandle connectSingleShot(std::function< void(Args...)> const &slot)
friend class ConnectionHandle
The main namespace of the KDBindings library.