diff --git a/modules/juce_core/containers/juce_ListenerList.h b/modules/juce_core/containers/juce_ListenerList.h index 5f657a0757..37ed43153e 100644 --- a/modules/juce_core/containers/juce_ListenerList.h +++ b/modules/juce_core/containers/juce_ListenerList.h @@ -114,6 +114,18 @@ public: }); } + /** Adds a listener that will be automatically removed again when the Guard is destroyed. + + Be very careful to ensure that the ErasedScopeGuard is destroyed or released before the + ListenerList is destroyed, otherwise the ErasedScopeGuard may attempt to dereference a + dangling pointer when it is destroyed, which will result in a crash. + */ + ErasedScopeGuard addScoped (ListenerClass& listenerToAdd) + { + add (&listenerToAdd); + return ErasedScopeGuard { [this, &listenerToAdd] { remove (&listenerToAdd); } }; + } + /** Returns the number of registered listeners. */ int size() const noexcept { return listeners.size(); } diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 788c74b027..3f4814317a 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -146,6 +146,7 @@ #include "misc/juce_Result.cpp" #include "misc/juce_Uuid.cpp" #include "misc/juce_ConsoleApplication.cpp" +#include "misc/juce_ScopeGuard.cpp" #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index f23eaed660..5279e18fbe 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -259,6 +259,7 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "containers/juce_ArrayBase.h" #include "containers/juce_Array.h" #include "containers/juce_LinkedListPointer.h" +#include "misc/juce_ScopeGuard.h" #include "containers/juce_ListenerList.h" #include "containers/juce_OwnedArray.h" #include "containers/juce_ReferenceCountedArray.h" diff --git a/modules/juce_core/misc/juce_Functional.h b/modules/juce_core/misc/juce_Functional.h index 040132f7f0..db5bd0d1ec 100644 --- a/modules/juce_core/misc/juce_Functional.h +++ b/modules/juce_core/misc/juce_Functional.h @@ -86,35 +86,6 @@ template struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; -template ScopeGuard (Fn) -> ScopeGuard; - #ifndef DOXYGEN namespace detail { diff --git a/modules/juce_core/misc/juce_ScopeGuard.cpp b/modules/juce_core/misc/juce_ScopeGuard.cpp new file mode 100644 index 0000000000..aee950d6e8 --- /dev/null +++ b/modules/juce_core/misc/juce_ScopeGuard.cpp @@ -0,0 +1,55 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +ErasedScopeGuard::ErasedScopeGuard (std::function d) + : detach (std::move (d)) {} + +ErasedScopeGuard::ErasedScopeGuard (ErasedScopeGuard&& other) noexcept + : detach (std::exchange (other.detach, nullptr)) {} + +ErasedScopeGuard& ErasedScopeGuard::operator= (ErasedScopeGuard&& other) noexcept +{ + ErasedScopeGuard token { std::move (other) }; + std::swap (token.detach, detach); + return *this; +} + +ErasedScopeGuard::~ErasedScopeGuard() noexcept +{ + reset(); +} + +void ErasedScopeGuard::reset() +{ + if (auto d = std::exchange (detach, nullptr)) + d(); +} + +void ErasedScopeGuard::release() +{ + detach = nullptr; +} + +} // namespace juce diff --git a/modules/juce_core/misc/juce_ScopeGuard.h b/modules/juce_core/misc/juce_ScopeGuard.h new file mode 100644 index 0000000000..8fbe3c48e9 --- /dev/null +++ b/modules/juce_core/misc/juce_ScopeGuard.h @@ -0,0 +1,113 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** An easy way to ensure that a function is called at the end of the current + scope. + + Usage: + @code + { + if (flag == true) + return; + + // While this code executes, flag is true e.g. to prevent reentrancy + flag = true; + // When we exit this scope, flag must be false + const ScopeGuard scope { [&] { flag = false; } }; + + if (checkInitialCondition()) + return; // Scope's lambda will fire here... + + if (checkCriticalCondition()) + throw std::runtime_error{}; // ...or here... + + doWorkHavingEstablishedPreconditions(); + } // ...or here! + @endcode + + @tags{Core} +*/ +template struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; +template ScopeGuard (Fn) -> ScopeGuard; + +/** + A ScopeGuard that uses a std::function internally to allow type erasure. + This can be handy; it allows lots of ErasedScopeGuards, all with different + callbacks, to be stored in a homogeneous container. + + An instance of this type will automatically call its callback when it is destroyed. + + ErasedScopeGuard has a few similarities with std::unique_ptr: + - Calling reset() on a unique_ptr destroys the object if it hasn't been destroyed yet + and puts the unique_ptr back into a default/null state; calling reset() on an + ErasedScopeGuard calls the callback if it hasn't been called yet and puts the Guard + back into a default/null state. + - Calling release() on a unique_ptr returns the unique_ptr back to a default state + without destroying the managed object; calling release() on an ErasedScopeGuard + returns the Guard back to a default state without calling the callback. + - Moving a unique_ptr transfers the responsibility of destroying the managed object + to another unique_ptr instance; moving an ErasedScopeGuard transfers the + responsibility of calling the callback to another Guard instance. +*/ +class [[nodiscard]] ErasedScopeGuard +{ +public: + /** Constructs an ErasedScopeGuard with no callback. */ + ErasedScopeGuard() = default; + + /** Constructs an ErasedScopeGuard that will call the provided callback + when the Guard is destroyed. + */ + explicit ErasedScopeGuard (std::function d); + + /** Constructs an instance that assumes responsibility for calling other's callback. */ + ErasedScopeGuard (ErasedScopeGuard&& other) noexcept; + + /** Calls the stored callback, if any, then assumes responsibility for calling + other's callback. After this call, other will be reset to its default state. + */ + ErasedScopeGuard& operator= (ErasedScopeGuard&& other) noexcept; + + /** Destructor, calls the callback assigned to this ScopeGuard. + */ + ~ErasedScopeGuard() noexcept; + + /** Calls the stored callback, if any, then resets this instance to its + default state. + */ + void reset(); + + /** Resets this instance to its default state without calling the stored + callback. + */ + void release(); + + JUCE_DECLARE_NON_COPYABLE (ErasedScopeGuard) + +private: + std::function detach; +}; + +} // namespace juce