sigh: allow disconnecting listeners during iterations (close #986)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#ifndef ENTT_SIGNAL_SIGH_HPP
|
||||
#define ENTT_SIGNAL_SIGH_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -169,8 +169,8 @@ public:
|
||||
* @param args Arguments to use to invoke listeners.
|
||||
*/
|
||||
void publish(Args... args) const {
|
||||
for(auto &&call: std::as_const(calls)) {
|
||||
call(args...);
|
||||
for(auto pos = calls.size(); pos; --pos) {
|
||||
calls[pos - 1u](args...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,9 +190,9 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void collect(Func func, Args... args) const {
|
||||
for(auto &&call: calls) {
|
||||
for(auto pos = calls.size(); pos; --pos) {
|
||||
if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) {
|
||||
call(args...);
|
||||
calls[pos - 1u](args...);
|
||||
|
||||
if constexpr(std::is_invocable_r_v<bool, Func>) {
|
||||
if(func()) {
|
||||
@@ -203,11 +203,11 @@ public:
|
||||
}
|
||||
} else {
|
||||
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
|
||||
if(func(call(args...))) {
|
||||
if(func(calls[pos - 1u](args...))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(call(args...));
|
||||
func(calls[pos - 1u](args...));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,6 +372,16 @@ class sink<sigh<Ret(Args...), Allocator>> {
|
||||
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void disconnect_if(Func callback) {
|
||||
for(auto pos = signal->calls.size(); pos; --pos) {
|
||||
if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
|
||||
elem = std::move(signal->calls.back());
|
||||
signal->calls.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a sink that is allowed to modify a given signal.
|
||||
@@ -418,10 +428,9 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Type>
|
||||
void disconnect(Type &&...value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance...);
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
|
||||
disconnect_if([&call](const auto &elem) { return elem == call; });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,9 +440,7 @@ public:
|
||||
*/
|
||||
void disconnect(const void *value_or_instance) {
|
||||
if(value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
|
||||
disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ struct const_nonconst_noexcept {
|
||||
mutable int cnt{0};
|
||||
};
|
||||
|
||||
void connect_and_auto_disconnect(entt::sigh<void(int &)> &sigh, const int &) {
|
||||
entt::sink sink{sigh};
|
||||
sink.connect<sigh_listener::f>();
|
||||
sink.disconnect<&connect_and_auto_disconnect>(sigh);
|
||||
}
|
||||
|
||||
TEST(SigH, Lifetime) {
|
||||
using signal = entt::sigh<void(void)>;
|
||||
|
||||
@@ -427,6 +433,32 @@ TEST(SigH, UnboundMemberFunction) {
|
||||
ASSERT_TRUE(listener.k);
|
||||
}
|
||||
|
||||
TEST(SigH, ConnectAndAutoDisconnect) {
|
||||
sigh_listener listener;
|
||||
entt::sigh<void(int &)> sigh;
|
||||
entt::sink sink{sigh};
|
||||
int v = 0;
|
||||
|
||||
sink.connect<&sigh_listener::g>(listener);
|
||||
sink.connect<&connect_and_auto_disconnect>(sigh);
|
||||
|
||||
ASSERT_FALSE(listener.k);
|
||||
ASSERT_EQ(sigh.size(), 2u);
|
||||
ASSERT_EQ(v, 0);
|
||||
|
||||
sigh.publish(v);
|
||||
|
||||
ASSERT_TRUE(listener.k);
|
||||
ASSERT_EQ(sigh.size(), 2u);
|
||||
ASSERT_EQ(v, 0);
|
||||
|
||||
sigh.publish(v);
|
||||
|
||||
ASSERT_FALSE(listener.k);
|
||||
ASSERT_EQ(sigh.size(), 2u);
|
||||
ASSERT_EQ(v, 42);
|
||||
}
|
||||
|
||||
TEST(SigH, CustomAllocator) {
|
||||
std::allocator<void (*)(int)> allocator;
|
||||
entt::sigh<void(int), decltype(allocator)> sigh{allocator};
|
||||
|
||||
Reference in New Issue
Block a user