From 5ce3c5a0cb5ea41113da16160a8363a7391432f0 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 8 Jun 2015 00:28:51 +0200 Subject: [PATCH] Core/Signal: Fix disconnection while iterating This is no longer an issue, you can now disconnect every signal as you wish even while the iteration Former-commit-id: 5caadaa860c62ce09f02f7555aa7c6adebc74fb9 --- include/Nazara/Core/Signal.hpp | 6 +++-- include/Nazara/Core/Signal.inl | 43 +++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index 8440a6602..4a62e0e38 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -26,7 +26,7 @@ class NzSignal class Connection; class ConnectionGuard; - NzSignal() = default; + NzSignal(); NzSignal(const NzSignal&) = delete; NzSignal(NzSignal&& signal); ~NzSignal() = default; @@ -50,6 +50,7 @@ class NzSignal using SlotPtr = std::shared_ptr; using SlotList = std::vector; + using SlotListIndex = typename SlotList::size_type; struct Slot { @@ -60,12 +61,13 @@ class NzSignal Callback callback; NzSignal* signal; - typename SlotList::size_type index; + SlotListIndex index; }; void Disconnect(const SlotPtr& slot); SlotList m_slots; + mutable SlotListIndex m_slotIterator; }; template diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index e915bd0dc..00986d1c8 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -6,6 +6,12 @@ #include #include +template +NzSignal::NzSignal() : +m_slotIterator(0) +{ +} + template NzSignal::NzSignal(NzSignal&& signal) { @@ -29,11 +35,15 @@ typename NzSignal::Connection NzSignal::Connect(Callback&& fun { NazaraAssert(func, "Invalid function"); + bool resetIt = (m_slotIterator >= m_slots.size()); + auto tempPtr = std::make_shared(this); tempPtr->callback = std::move(func); tempPtr->index = m_slots.size(); m_slots.emplace_back(std::move(tempPtr)); + if (resetIt) + m_slotIterator = m_slots.size(); return Connection(m_slots.back()); } @@ -81,14 +91,15 @@ typename NzSignal::Connection NzSignal::Connect(const O* objec template void NzSignal::operator()(Args... args) const { - for (const SlotPtr& slot : m_slots) - slot->callback(args...); + for (m_slotIterator = 0; m_slotIterator < m_slots.size(); ++m_slotIterator) + m_slots[m_slotIterator]->callback(args...); } template NzSignal& NzSignal::operator=(NzSignal&& signal) { m_slots = std::move(signal.m_slots); + m_slotIterator = signal.m_slotIterator; // We need to update the signal pointer inside of each slot for (SlotPtr& slot : m_slots) @@ -105,12 +116,32 @@ void NzSignal::Disconnect(const SlotPtr& slot) // "Swap this slot with the last one and pop" idiom // This will preserve slot indexes - SlotPtr& current = m_slots[slot->index]; - std::swap(current, m_slots.back()); + // Can we safely "remove" this slot? + if (m_slotIterator >= m_slots.size()-1 || slot->index > m_slotIterator) + { + // Yes we can + SlotPtr& newSlot = m_slots[slot->index]; + newSlot = std::move(m_slots.back()); + newSlot->index = slot->index; //< Update the moved slot index before resizing (imagine this is the last one) + } + else + { + // Nope, let's be tricky + SlotPtr& current = m_slots[m_slotIterator]; + SlotPtr& newSlot = m_slots[slot->index]; + + newSlot = std::move(current); + newSlot->index = slot->index; //< Update the moved slot index + + current = std::move(m_slots.back()); + current->index = m_slotIterator; //< Update the moved slot index + + --m_slotIterator; + } + + // Pop the last entry (where we moved our slot) m_slots.pop_back(); - - current->index = slot->index; //< Update the moved slot index }