From a3228b6323f1c4086d9737a7bbc325c3c3da10e7 Mon Sep 17 00:00:00 2001 From: Robert Andrzejuk Date: Wed, 23 Apr 2025 23:26:57 +0200 Subject: [PATCH 01/12] Prepare for scope implementation --- include/beman/scope/scope.hpp | 79 ++++++++++++++++++++++++----------- tests/scope_exit.test.cpp | 12 +++--- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 844437c..13e9568 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -3,13 +3,53 @@ #ifndef BEMAN_SCOPE_HPP #define BEMAN_SCOPE_HPP + #include -#include +#include + + +#if (!(defined(__cpp_concepts) || !(defined(__cpp_lib_concepts)))) +static_assert(false, "C++20 Concepts Required"); +#endif + -#include +#ifdef BEMAN_SCOPE_USE_STD_EXPERIMENTAL + + #include namespace beman::scope { + +template +using scope_exit = std::experimental::scope_exit; + +template +using scope_fail = std::experimental::scope_fail; + +template +using scope_success = std::experimental::scope_success; + + +template +using unique_resource = std::experimental::unique_resource; + + +template> +unique_resource, std::decay_t> make_unique_resource_checked(R&& r, const S& invalid, D&& d) + noexcept(noexcept( + std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) +{ + return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); +} + +} + +#else // ! BEMAN_SCOPE__USE_STD_EXPERIMENTAL + +namespace beman::scope { + +//================================================================================================== +// // -- 7.6.7 Feature test macro -- // // __cpp_lib_scope @@ -18,39 +58,24 @@ namespace beman::scope { // -- 7.5.1 Header synopsis [scope.syn] -- // // namespace std { - +// // template // class scope_exit; -template -using scope_exit = std::experimental::scope_exit; - +// // template // class scope_fail; -template -using scope_fail = std::experimental::scope_fail; - +// // template // class scope_success; -template -using scope_success = std::experimental::scope_success; - +// // template // class unique_resource; -template -using unique_resource = std::experimental::unique_resource; - +// // // factory function // template > // unique_resource, decay_t> // make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(see below); - -template > -unique_resource, std::decay_t> -make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( - std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) { - return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); -} - +// // } // namespace std // @@ -80,7 +105,8 @@ make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( // // template // scope_guard(EF) -> scope_guard; -// + + // -- 7.6.1 Class template unique_resource [scope.unique_resource.class] -- // // template @@ -113,7 +139,12 @@ make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( // // template // unique_resource(R, D) -> unique_resource; +// +//================================================================================================== + } // namespace beman::scope +#endif // BEMAN_SCOPE__USE_STD_EXPERIMENTAL + #endif // BEMAN_SCOPE_HPP diff --git a/tests/scope_exit.test.cpp b/tests/scope_exit.test.cpp index 072ac38..83e6a6e 100644 --- a/tests/scope_exit.test.cpp +++ b/tests/scope_exit.test.cpp @@ -9,7 +9,9 @@ #define CATCH_CONFIG_MAIN #include -using namespace beman::scope; + +using beman::scope::scope_exit; + TEST_CASE("scope_exit runs handler on normal scope exit", "[scope_exit]") { bool cleanup_ran = false; @@ -57,7 +59,7 @@ TEST_CASE("scope_exit does not run handler if released", "[scope_exit]") { } TEST_CASE("scope_exit supports move semantics", "[scope_exit]") { - bool cleanup_ran = false; + bool cleanup_ran_count = false; { scope_exit guard1([&]() { @@ -73,17 +75,17 @@ TEST_CASE("scope_exit supports move semantics", "[scope_exit]") { } TEST_CASE("moved-from scope_exit does not trigger handler", "[scope_exit]") { - bool cleanup_ran = false; + bool cleanup_ran_count = 0; { scope_exit guard1([&]() { - cleanup_ran = true; + ++cleanup_ran; }); [[maybe_unused]] auto guard2 = std::move(guard1); // guard1 is now a no-op } - REQUIRE(cleanup_ran == true); // cleanup still runs — but from guard2 + REQUIRE(cleanup_ran_count == 1 ); // cleanup still runs — but from guard2 } TEST_CASE("scope_exit supports noexcept lambdas", "[scope_exit][advanced]") { From 5876b4a5e83b30214553023a7255aa4520621b19 Mon Sep 17 00:00:00 2001 From: Robert Andrzejuk Date: Thu, 24 Apr 2025 01:11:43 +0200 Subject: [PATCH 02/12] scope_guard and scope_exit specialization --- include/beman/scope/scope.hpp | 285 ++++++++++++++++++++++++++++++++++ tests/scope_exit.test.cpp | 6 +- 2 files changed, 288 insertions(+), 3 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 13e9568..192c365 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -4,6 +4,7 @@ #define BEMAN_SCOPE_HPP +#include #include #include @@ -142,6 +143,290 @@ namespace beman::scope { // //================================================================================================== +//Template argument `ScopeExitFunc` shall be +// - a function object type([function.objects]), +// - lvalue reference to function, +// - or lvalue reference to function object type. +// +// If `ScopeExitFunc` is an object type, it shall meet the requirements of Cpp17Destructible(Table 30). +// Given an lvalue g of type remove_reference_t, the expression g() shall be well- formed. + +//================================================================================================== + + +// --- Concepts --- +template +concept invocable_return = std::invocable && std::convertible_to, R>; + + +template +concept scope_exit_function = invocable_return + && (std::is_nothrow_move_constructible_v || std::is_copy_constructible_v); + +template +concept scope_function_invoke_check = invocable_return; + +template +concept HasRelease = requires (T t) { + { t.release() } -> std::same_as; + }; + +template +concept HasStaticRelease = requires { + { T::release() } -> std::same_as; + }; + + +// --- Enum --- + +enum class exception_during_constuction_behaviour : std::uint8_t +{ + dont_invoke_exit_func, + invoke_exit_func, +}; + +//================================================================================================== + +// --- `scope_guard` - primary template --- + +template +class [[nodiscard]] scope_guard; + + +//================================================================================================== + +// --- General definition --- + +template +class [[nodiscard]] scope_guard +{ +public: + //The constructor parameter `exit_func` in the following constructors shall be : + // - a reference to a function + // - or a reference to a function object([function.objects]) + // + + // If EFP is not an lvalue reference type and is_nothrow_constructible_v is true, + // initialize exit_function with std::forward(f); + // otherwise initialize exit_function with f. + + // scope_fail / scope_exit + // If the initialization of exit_function throws an exception, calls f(). + + // scope_success + // [Note: If initialization of exit_function fails, f() won’t be called. —end note] + + + template + constexpr scope_guard(EF&& exit_func, CHKR&& invoke_checker) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + try + : m_exit_func { std::forward(exit_func) }, + m_invoke_checker { std::forward(invoke_checker) } + {} + catch (...) + { + if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) + { + exit_func(); + + // To throw? or not to throw? + throw; + } + } + + + template + explicit constexpr scope_guard(EF&& exit_func) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + requires (std::is_default_constructible_v && !std::is_same_v, scope_guard>) + try + : m_exit_func { std::forward(exit_func) } + {} + catch (...) + { + if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) + { + exit_func(); + + // To throw? or not to throw? + throw; + } + } + + + constexpr scope_guard(scope_guard&& rhs) noexcept(std::is_nothrow_move_constructible_v + && std::is_nothrow_move_constructible_v) + requires (HasRelease || HasStaticRelease) + : m_exit_func { std::move(rhs.m_exit_func) }, + m_invoke_checker { std::move(rhs.m_invoke_checker) } + { + // TODO: This does not work corectly for a shared invoke checker + // After a move will disable all. + + if constexpr (HasStaticRelease) + { + InvokeChecker::release(); + } + else + { + rhs.release(); + } + } + + + scope_guard(const scope_guard&) = delete; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&& rhs) = delete; + + + constexpr ~scope_guard() noexcept(noexcept(m_exit_func()) && noexcept(m_invoke_checker())) + { + if (m_invoke_checker()) + { + m_exit_func(); + } + } + + + InvokeChecker& invoke_checker() & noexcept + { + return m_invoke_checker; + } + + constexpr void release() noexcept + // Shouldn't this "noexcept" be dependent on the noexcept of the release function? how?? + requires (HasRelease || HasStaticRelease) + { + if constexpr (HasRelease) + { + m_invoke_checker.release(); + } + else + { + InvokeChecker::release(); + } + } + +private: + ScopeExitFunc m_exit_func; + InvokeChecker m_invoke_checker; +}; + +//====== + +// --- Specializations for no releaser + +template +class [[nodiscard]] scope_guard +{ + ScopeExitFunc m_exit_func; + +public: + template + explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) + requires (!std::is_same_v, scope_guard>) + try + : m_exit_func(std::forward(exit_func)) + {} + catch (...) + { + exit_func(); + + throw; + } + + + scope_guard(const scope_guard&) = delete; + scope_guard(scope_guard&&) = delete; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&&) = delete; + + + constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) + { + m_exit_func(); + } +}; + +//====== + +template +class [[nodiscard]] scope_guard +{ + ScopeExitFunc m_exit_func; + +public: + template + explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) + requires (!std::is_same_v, scope_guard>) + : m_exit_func(std::forward(exit_func)) + {} + + + scope_guard(const scope_guard&) = delete; + scope_guard(scope_guard&&) = delete; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&&) = delete; + + + constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) + { + m_exit_func(); + } +}; + +//================================================================================================== + +// --- Deduction guides --- + +template + requires (scope_exit_function && ( scope_function_invoke_check )) +scope_guard(ExitFunc&&, InvokeChecker&&) -> scope_guard, std::decay_t, ecdb>; + + +template + requires (scope_exit_function + && ( scope_function_invoke_check || std::is_void_v )) +scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ecdb>; + +//================================================================================================== + +class Releaser +{ +public: + bool operator()() const + { + return m_can_invoke; + } + + + void release() + { + m_can_invoke = false; + } + +private: + bool m_can_invoke = true; +}; + + +//================================================================================================== + +// --- type aliases --- + +template +using scope_exit = scope_guard; + } // namespace beman::scope diff --git a/tests/scope_exit.test.cpp b/tests/scope_exit.test.cpp index 83e6a6e..65acf7a 100644 --- a/tests/scope_exit.test.cpp +++ b/tests/scope_exit.test.cpp @@ -59,7 +59,7 @@ TEST_CASE("scope_exit does not run handler if released", "[scope_exit]") { } TEST_CASE("scope_exit supports move semantics", "[scope_exit]") { - bool cleanup_ran_count = false; + bool cleanup_ran = false; { scope_exit guard1([&]() { @@ -75,11 +75,11 @@ TEST_CASE("scope_exit supports move semantics", "[scope_exit]") { } TEST_CASE("moved-from scope_exit does not trigger handler", "[scope_exit]") { - bool cleanup_ran_count = 0; + int cleanup_ran_count = 0; { scope_exit guard1([&]() { - ++cleanup_ran; + ++cleanup_ran_count; }); [[maybe_unused]] auto guard2 = std::move(guard1); // guard1 is now a no-op From 6d77a7af51504deb29033086843d267079649f6e Mon Sep 17 00:00:00 2001 From: Robert Andrzejuk Date: Thu, 24 Apr 2025 01:32:26 +0200 Subject: [PATCH 03/12] scope_success --- include/beman/scope/scope.hpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 192c365..00269b3 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -5,6 +5,7 @@ #include +#include #include #include @@ -420,6 +421,29 @@ class Releaser }; +//====== + +class ReleasableExecuteWhenNoException +{ +public: + using DontInvokeOnCreationException = void; + + + [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) + { + return m_uncaught_on_creation >= std::uncaught_exceptions(); + } + + void release() + { + m_uncaught_on_creation = INT_MIN; + } + +private: + int m_uncaught_on_creation = std::uncaught_exceptions(); +}; + + //================================================================================================== // --- type aliases --- @@ -428,6 +452,12 @@ template using scope_exit = scope_guard; +template +using scope_success = scope_guard; + + } // namespace beman::scope #endif // BEMAN_SCOPE__USE_STD_EXPERIMENTAL From 267239700ef693250383e3b4a4ed37d1b196650e Mon Sep 17 00:00:00 2001 From: Robert Andrzejuk Date: Thu, 24 Apr 2025 01:36:39 +0200 Subject: [PATCH 04/12] scope_fail --- include/beman/scope/scope.hpp | 24 ++++++++++++++++++++++++ tests/scope_fail.test.cpp | 7 ++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 00269b3..50b8b54 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -444,6 +444,27 @@ class ReleasableExecuteWhenNoException }; +//====== + +class ReleasableExecuteOnlyWhenException +{ +public: + [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) + { + return m_uncaught_on_creation < std::uncaught_exceptions(); + } + + + void release() + { + m_uncaught_on_creation = INT_MAX; + } + +private: + int m_uncaught_on_creation = std::uncaught_exceptions(); +}; + + //================================================================================================== // --- type aliases --- @@ -457,6 +478,9 @@ using scope_success = scope_guard; +template +using scope_fail = + scope_guard; } // namespace beman::scope diff --git a/tests/scope_fail.test.cpp b/tests/scope_fail.test.cpp index 3497a6a..47f4530 100644 --- a/tests/scope_fail.test.cpp +++ b/tests/scope_fail.test.cpp @@ -9,7 +9,8 @@ #define CATCH_CONFIG_MAIN #include -using namespace beman::scope; +using beman::scope::scope_fail; +using beman::scope::scope_success; TEST_CASE("scope_fail runs handler on exception", "[scope_fail]") { bool triggered = false; @@ -187,11 +188,11 @@ TEST_CASE("scope_fail coexists with scope_success", "[scope_fail][advanced]") { std::string output; try { - std::experimental::scope_success success([&]() { + scope_success success([&]() { output += "success "; }); - std::experimental::scope_fail fail([&]() { + scope_fail fail([&]() { output += "fail "; }); From 86c3f6c6e8b5f92716a1e42296840551dc88ab53 Mon Sep 17 00:00:00 2001 From: Robert Andrzejuk Date: Thu, 24 Apr 2025 02:07:34 +0200 Subject: [PATCH 05/12] reformat --- include/beman/scope/scope.hpp | 309 +++++++++++++--------------------- 1 file changed, 114 insertions(+), 195 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 50b8b54..ac5f0ac 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -3,48 +3,41 @@ #ifndef BEMAN_SCOPE_HPP #define BEMAN_SCOPE_HPP - #include #include #include #include - #if (!(defined(__cpp_concepts) || !(defined(__cpp_lib_concepts)))) static_assert(false, "C++20 Concepts Required"); #endif - #ifdef BEMAN_SCOPE_USE_STD_EXPERIMENTAL - #include +#include namespace beman::scope { - -template +template using scope_exit = std::experimental::scope_exit; -template +template using scope_fail = std::experimental::scope_fail; -template +template using scope_success = std::experimental::scope_success; - -template +template using unique_resource = std::experimental::unique_resource; - -template> -unique_resource, std::decay_t> make_unique_resource_checked(R&& r, const S& invalid, D&& d) - noexcept(noexcept( - std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) -{ +template > +unique_resource, std::decay_t> +make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( + std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) { return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); } -} +} // namespace beman::scope #else // ! BEMAN_SCOPE__USE_STD_EXPERIMENTAL @@ -108,7 +101,6 @@ namespace beman::scope { // template // scope_guard(EF) -> scope_guard; - // -- 7.6.1 Class template unique_resource [scope.unique_resource.class] -- // // template @@ -144,44 +136,40 @@ namespace beman::scope { // //================================================================================================== -//Template argument `ScopeExitFunc` shall be -// - a function object type([function.objects]), -// - lvalue reference to function, -// - or lvalue reference to function object type. +// Template argument `ScopeExitFunc` shall be +// - a function object type([function.objects]), +// - lvalue reference to function, +// - or lvalue reference to function object type. // -// If `ScopeExitFunc` is an object type, it shall meet the requirements of Cpp17Destructible(Table 30). -// Given an lvalue g of type remove_reference_t, the expression g() shall be well- formed. +// If `ScopeExitFunc` is an object type, it shall meet the requirements of Cpp17Destructible(Table 30). +// Given an lvalue g of type remove_reference_t, the expression g() shall be well- formed. //================================================================================================== - // --- Concepts --- -template +template concept invocable_return = std::invocable && std::convertible_to, R>; +template +concept scope_exit_function = + invocable_return && (std::is_nothrow_move_constructible_v || std::is_copy_constructible_v); -template -concept scope_exit_function = invocable_return - && (std::is_nothrow_move_constructible_v || std::is_copy_constructible_v); - -template +template concept scope_function_invoke_check = invocable_return; -template -concept HasRelease = requires (T t) { - { t.release() } -> std::same_as; - }; +template +concept HasRelease = requires(T t) { + { t.release() } -> std::same_as; +}; -template +template concept HasStaticRelease = requires { - { T::release() } -> std::same_as; - }; - + { T::release() } -> std::same_as; +}; // --- Enum --- -enum class exception_during_constuction_behaviour : std::uint8_t -{ +enum class exception_during_constuction_behaviour : std::uint8_t { dont_invoke_exit_func, invoke_exit_func, }; @@ -190,26 +178,24 @@ enum class exception_during_constuction_behaviour : std::uint8_t // --- `scope_guard` - primary template --- -template +template class [[nodiscard]] scope_guard; - //================================================================================================== // --- General definition --- -template -class [[nodiscard]] scope_guard -{ -public: - //The constructor parameter `exit_func` in the following constructors shall be : - // - a reference to a function - // - or a reference to a function object([function.objects]) +template +class [[nodiscard]] scope_guard { + public: + // The constructor parameter `exit_func` in the following constructors shall be : + // - a reference to a function + // - or a reference to a function object([function.objects]) // // If EFP is not an lvalue reference type and is_nothrow_constructible_v is true, @@ -222,18 +208,13 @@ class [[nodiscard]] scope_guard - constexpr scope_guard(EF&& exit_func, CHKR&& invoke_checker) - noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) - try - : m_exit_func { std::forward(exit_func) }, - m_invoke_checker { std::forward(invoke_checker) } - {} - catch (...) - { - if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) - { + template + constexpr scope_guard(EF&& exit_func, + CHKR&& invoke_checker) noexcept(std::is_nothrow_constructible_v && + std::is_nothrow_constructible_v) try + : m_exit_func{std::forward(exit_func)}, m_invoke_checker{std::forward(invoke_checker)} { + } catch (...) { + if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) { exit_func(); // To throw? or not to throw? @@ -241,18 +222,13 @@ class [[nodiscard]] scope_guard - explicit constexpr scope_guard(EF&& exit_func) - noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) - requires (std::is_default_constructible_v && !std::is_same_v, scope_guard>) - try - : m_exit_func { std::forward(exit_func) } - {} - catch (...) - { - if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) - { + template + explicit constexpr scope_guard(EF&& exit_func) noexcept(std::is_nothrow_constructible_v && + std::is_nothrow_constructible_v) + requires(std::is_default_constructible_v && !std::is_same_v, scope_guard>) + try : m_exit_func{std::forward(exit_func)} { + } catch (...) { + if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) { exit_func(); // To throw? or not to throw? @@ -260,61 +236,44 @@ class [[nodiscard]] scope_guard - && std::is_nothrow_move_constructible_v) - requires (HasRelease || HasStaticRelease) - : m_exit_func { std::move(rhs.m_exit_func) }, - m_invoke_checker { std::move(rhs.m_invoke_checker) } - { + constexpr scope_guard(scope_guard&& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires(HasRelease || HasStaticRelease) + : m_exit_func{std::move(rhs.m_exit_func)}, m_invoke_checker{std::move(rhs.m_invoke_checker)} { // TODO: This does not work corectly for a shared invoke checker // After a move will disable all. - if constexpr (HasStaticRelease) - { + if constexpr (HasStaticRelease) { InvokeChecker::release(); - } - else - { + } else { rhs.release(); } } - scope_guard(const scope_guard&) = delete; scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(scope_guard&& rhs) = delete; - - constexpr ~scope_guard() noexcept(noexcept(m_exit_func()) && noexcept(m_invoke_checker())) - { - if (m_invoke_checker()) - { + constexpr ~scope_guard() noexcept(noexcept(m_exit_func()) && noexcept(m_invoke_checker())) { + if (m_invoke_checker()) { m_exit_func(); } } - - InvokeChecker& invoke_checker() & noexcept - { - return m_invoke_checker; - } + InvokeChecker& invoke_checker() & noexcept { return m_invoke_checker; } constexpr void release() noexcept - // Shouldn't this "noexcept" be dependent on the noexcept of the release function? how?? - requires (HasRelease || HasStaticRelease) + // Shouldn't this "noexcept" be dependent on the noexcept of the release function? how?? + requires(HasRelease || HasStaticRelease) { - if constexpr (HasRelease) - { + if constexpr (HasRelease) { m_invoke_checker.release(); - } - else - { + } else { InvokeChecker::release(); } } -private: + private: ScopeExitFunc m_exit_func; InvokeChecker m_invoke_checker; }; @@ -323,164 +282,124 @@ class [[nodiscard]] scope_guard -class [[nodiscard]] scope_guard -{ +template +class [[nodiscard]] scope_guard { ScopeExitFunc m_exit_func; -public: - template + public: + template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) - requires (!std::is_same_v, scope_guard>) - try - : m_exit_func(std::forward(exit_func)) - {} - catch (...) - { + requires(!std::is_same_v, scope_guard>) + try : m_exit_func(std::forward(exit_func)) { + } catch (...) { exit_func(); throw; } - scope_guard(const scope_guard&) = delete; scope_guard(scope_guard&&) = delete; scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(scope_guard&&) = delete; - - constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) - { - m_exit_func(); - } + constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) { m_exit_func(); } }; //====== -template -class [[nodiscard]] scope_guard -{ +template +class [[nodiscard]] scope_guard { ScopeExitFunc m_exit_func; -public: - template + public: + template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) - requires (!std::is_same_v, scope_guard>) - : m_exit_func(std::forward(exit_func)) - {} - + requires(!std::is_same_v, scope_guard>) + : m_exit_func(std::forward(exit_func)) {} scope_guard(const scope_guard&) = delete; scope_guard(scope_guard&&) = delete; scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(scope_guard&&) = delete; - - constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) - { - m_exit_func(); - } + constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) { m_exit_func(); } }; //================================================================================================== // --- Deduction guides --- -template - requires (scope_exit_function && ( scope_function_invoke_check )) +template + requires(scope_exit_function && (scope_function_invoke_check)) scope_guard(ExitFunc&&, InvokeChecker&&) -> scope_guard, std::decay_t, ecdb>; - -template - requires (scope_exit_function - && ( scope_function_invoke_check || std::is_void_v )) +template + requires(scope_exit_function && + (scope_function_invoke_check || std::is_void_v)) scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ecdb>; //================================================================================================== -class Releaser -{ -public: - bool operator()() const - { - return m_can_invoke; - } - +class Releaser { + public: + bool operator()() const { return m_can_invoke; } - void release() - { - m_can_invoke = false; - } + void release() { m_can_invoke = false; } -private: + private: bool m_can_invoke = true; }; - //====== -class ReleasableExecuteWhenNoException -{ -public: +class ReleasableExecuteWhenNoException { + public: using DontInvokeOnCreationException = void; - - [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) - { + [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { return m_uncaught_on_creation >= std::uncaught_exceptions(); } - void release() - { - m_uncaught_on_creation = INT_MIN; - } + void release() { m_uncaught_on_creation = INT_MIN; } -private: + private: int m_uncaught_on_creation = std::uncaught_exceptions(); }; - //====== -class ReleasableExecuteOnlyWhenException -{ -public: - [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) - { +class ReleasableExecuteOnlyWhenException { + public: + [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { return m_uncaught_on_creation < std::uncaught_exceptions(); } + void release() { m_uncaught_on_creation = INT_MAX; } - void release() - { - m_uncaught_on_creation = INT_MAX; - } - -private: + private: int m_uncaught_on_creation = std::uncaught_exceptions(); }; - //================================================================================================== // --- type aliases --- -template +template using scope_exit = scope_guard; - -template +template using scope_success = scope_guard; -template -using scope_fail = - scope_guard; +template +using scope_fail = scope_guard; } // namespace beman::scope From 59baf8cfdd557a4ad39981247bc2a26b06e57882 Mon Sep 17 00:00:00 2001 From: JeffGarland Date: Mon, 19 May 2025 04:40:32 -0700 Subject: [PATCH 06/12] fix compilation - restore unique_resource, fix exception_during_construction, and limits --- include/beman/scope/scope.hpp | 63 ++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index ac5f0ac..62df30c 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -7,14 +7,16 @@ #include #include #include +#include #if (!(defined(__cpp_concepts) || !(defined(__cpp_lib_concepts)))) static_assert(false, "C++20 Concepts Required"); #endif +#include //todo unconditional for unique_resource + #ifdef BEMAN_SCOPE_USE_STD_EXPERIMENTAL -#include namespace beman::scope { @@ -27,9 +29,28 @@ using scope_fail = std::experimental::scope_fail; template using scope_success = std::experimental::scope_success; +// todo temporary +// template +// using unique_resource = std::experimental::unique_resource; + +// template > +// unique_resource, std::decay_t> +// make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( +// std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) { +// return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); +//} + +} // namespace beman::scope + +#else // ! BEMAN_SCOPE__USE_STD_EXPERIMENTAL + +namespace beman::scope { + +// todo temporary template using unique_resource = std::experimental::unique_resource; - + +// todo temporary template > unique_resource, std::decay_t> make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( @@ -37,12 +58,6 @@ make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); } -} // namespace beman::scope - -#else // ! BEMAN_SCOPE__USE_STD_EXPERIMENTAL - -namespace beman::scope { - //================================================================================================== // // -- 7.6.7 Feature test macro -- @@ -169,9 +184,9 @@ concept HasStaticRelease = requires { // --- Enum --- -enum class exception_during_constuction_behaviour : std::uint8_t { +enum class exception_during_construction_behaviour { dont_invoke_exit_func, - invoke_exit_func, + invoke_exit_func }; //================================================================================================== @@ -180,8 +195,8 @@ enum class exception_during_constuction_behaviour : std::uint8_t { template + exception_during_construction_behaviour ConstructionExceptionBehavior = + exception_during_construction_behaviour::invoke_exit_func> class [[nodiscard]] scope_guard; //================================================================================================== @@ -190,7 +205,7 @@ class [[nodiscard]] scope_guard; template + exception_during_construction_behaviour ConstructionExceptionBehavior> class [[nodiscard]] scope_guard { public: // The constructor parameter `exit_func` in the following constructors shall be : @@ -214,7 +229,7 @@ class [[nodiscard]] scope_guard) try : m_exit_func{std::forward(exit_func)}, m_invoke_checker{std::forward(invoke_checker)} { } catch (...) { - if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) { + if constexpr (ConstructionExceptionBehavior == exception_during_construction_behaviour::invoke_exit_func) { exit_func(); // To throw? or not to throw? @@ -228,7 +243,7 @@ class [[nodiscard]] scope_guard && !std::is_same_v, scope_guard>) try : m_exit_func{std::forward(exit_func)} { } catch (...) { - if constexpr (ConstructionExceptionBehavior == exception_during_constuction_behaviour::invoke_exit_func) { + if constexpr (ConstructionExceptionBehavior == exception_during_construction_behaviour::invoke_exit_func) { exit_func(); // To throw? or not to throw? @@ -283,7 +298,7 @@ class [[nodiscard]] scope_guard -class [[nodiscard]] scope_guard { +class [[nodiscard]] scope_guard { ScopeExitFunc m_exit_func; public: @@ -308,7 +323,7 @@ class [[nodiscard]] scope_guard -class [[nodiscard]] scope_guard { +class [[nodiscard]] scope_guard { ScopeExitFunc m_exit_func; public: @@ -331,13 +346,13 @@ class [[nodiscard]] scope_guard + exception_during_construction_behaviour ecdb = exception_during_construction_behaviour::invoke_exit_func> requires(scope_exit_function && (scope_function_invoke_check)) scope_guard(ExitFunc&&, InvokeChecker&&) -> scope_guard, std::decay_t, ecdb>; template + exception_during_construction_behaviour ecdb = exception_during_construction_behaviour::invoke_exit_func> requires(scope_exit_function && (scope_function_invoke_check || std::is_void_v)) scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ecdb>; @@ -364,7 +379,7 @@ class ReleasableExecuteWhenNoException { return m_uncaught_on_creation >= std::uncaught_exceptions(); } - void release() { m_uncaught_on_creation = INT_MIN; } + void release() { m_uncaught_on_creation = std::numeric_limits::min(); } private: int m_uncaught_on_creation = std::uncaught_exceptions(); @@ -378,7 +393,7 @@ class ReleasableExecuteOnlyWhenException { return m_uncaught_on_creation < std::uncaught_exceptions(); } - void release() { m_uncaught_on_creation = INT_MAX; } + void release() { m_uncaught_on_creation = std::numeric_limits::max(); } private: int m_uncaught_on_creation = std::uncaught_exceptions(); @@ -389,17 +404,17 @@ class ReleasableExecuteOnlyWhenException { // --- type aliases --- template -using scope_exit = scope_guard; +using scope_exit = scope_guard; template using scope_success = scope_guard; + exception_during_construction_behaviour::dont_invoke_exit_func>; template using scope_fail = scope_guard; + exception_during_construction_behaviour::invoke_exit_func>; } // namespace beman::scope From a81b6eaf5b6cf230111b57304aa0abdaeff31bc1 Mon Sep 17 00:00:00 2001 From: JeffGarland Date: Mon, 19 May 2025 04:48:51 -0700 Subject: [PATCH 07/12] remove all m_ adornement - rename invoke_checker member to invoke_check_func --- include/beman/scope/scope.hpp | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 62df30c..7d630c9 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -227,7 +227,7 @@ class [[nodiscard]] scope_guard && std::is_nothrow_constructible_v) try - : m_exit_func{std::forward(exit_func)}, m_invoke_checker{std::forward(invoke_checker)} { + : exit_func{std::forward(exit_func)}, invoke_check_func{std::forward(invoke_checker)} { } catch (...) { if constexpr (ConstructionExceptionBehavior == exception_during_construction_behaviour::invoke_exit_func) { exit_func(); @@ -241,7 +241,7 @@ class [[nodiscard]] scope_guard && std::is_nothrow_constructible_v) requires(std::is_default_constructible_v && !std::is_same_v, scope_guard>) - try : m_exit_func{std::forward(exit_func)} { + try : exit_func{std::forward(exit_func)} { } catch (...) { if constexpr (ConstructionExceptionBehavior == exception_during_construction_behaviour::invoke_exit_func) { exit_func(); @@ -254,7 +254,7 @@ class [[nodiscard]] scope_guard && std::is_nothrow_move_constructible_v) requires(HasRelease || HasStaticRelease) - : m_exit_func{std::move(rhs.m_exit_func)}, m_invoke_checker{std::move(rhs.m_invoke_checker)} { + : exit_func{std::move(rhs.exit_func)}, invoke_check_func{std::move(rhs.invoke_check_func)} { // TODO: This does not work corectly for a shared invoke checker // After a move will disable all. @@ -269,28 +269,28 @@ class [[nodiscard]] scope_guard || HasStaticRelease) { if constexpr (HasRelease) { - m_invoke_checker.release(); + invoke_check_func.release(); } else { InvokeChecker::release(); } } private: - ScopeExitFunc m_exit_func; - InvokeChecker m_invoke_checker; + ScopeExitFunc exit_func; + InvokeChecker invoke_check_func; }; //====== @@ -299,13 +299,13 @@ class [[nodiscard]] scope_guard class [[nodiscard]] scope_guard { - ScopeExitFunc m_exit_func; + ScopeExitFunc exit_func; public: template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) requires(!std::is_same_v, scope_guard>) - try : m_exit_func(std::forward(exit_func)) { + try : exit_func(std::forward(exit_func)) { } catch (...) { exit_func(); @@ -317,27 +317,27 @@ class [[nodiscard]] scope_guard class [[nodiscard]] scope_guard { - ScopeExitFunc m_exit_func; + ScopeExitFunc exit_func; public: template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) requires(!std::is_same_v, scope_guard>) - : m_exit_func(std::forward(exit_func)) {} + : exit_func(std::forward(exit_func)) {} scope_guard(const scope_guard&) = delete; scope_guard(scope_guard&&) = delete; scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(scope_guard&&) = delete; - constexpr ~scope_guard() noexcept(noexcept(m_exit_func())) { m_exit_func(); } + constexpr ~scope_guard() noexcept(noexcept(exit_func())) { exit_func(); } }; //================================================================================================== @@ -361,12 +361,12 @@ scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ec class Releaser { public: - bool operator()() const { return m_can_invoke; } + bool operator()() const { return can_invoke; } - void release() { m_can_invoke = false; } + void release() { can_invoke = false; } private: - bool m_can_invoke = true; + bool can_invoke = true; }; //====== @@ -376,13 +376,13 @@ class ReleasableExecuteWhenNoException { using DontInvokeOnCreationException = void; [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { - return m_uncaught_on_creation >= std::uncaught_exceptions(); + return uncaught_on_creation >= std::uncaught_exceptions(); } - void release() { m_uncaught_on_creation = std::numeric_limits::min(); } + void release() { uncaught_on_creation = std::numeric_limits::min(); } private: - int m_uncaught_on_creation = std::uncaught_exceptions(); + int uncaught_on_creation = std::uncaught_exceptions(); }; //====== @@ -390,13 +390,13 @@ class ReleasableExecuteWhenNoException { class ReleasableExecuteOnlyWhenException { public: [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { - return m_uncaught_on_creation < std::uncaught_exceptions(); + return uncaught_on_creation < std::uncaught_exceptions(); } - void release() { m_uncaught_on_creation = std::numeric_limits::max(); } + void release() { uncaught_on_creation = std::numeric_limits::max(); } private: - int m_uncaught_on_creation = std::uncaught_exceptions(); + int uncaught_on_creation = std::uncaught_exceptions(); }; //================================================================================================== From c9f20515a1217ecfc0c938542c9a1e56ccd55661 Mon Sep 17 00:00:00 2001 From: JeffGarland Date: Mon, 19 May 2025 04:56:59 -0700 Subject: [PATCH 08/12] Convert a couple of comments to doxygen format for future docs --- include/beman/scope/scope.hpp | 39 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 7d630c9..12390e3 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -201,28 +201,33 @@ class [[nodiscard]] scope_guard; //================================================================================================== -// --- General definition --- - +/** Generalized scope guard template + * This template provides the general behaviors required for more concrete instances of scope types. + * @tparam ScopeExitFunction callable function that is conditionally invoked at the end of the scope. + * @tparam InvokeChecker callable function that handles checking if callback should be called on scope exit. + * @tparam ConstructionExceptionBehavior callable function that defines the behavior if an exception occurs + * on the construction. + */ template class [[nodiscard]] scope_guard { public: - // The constructor parameter `exit_func` in the following constructors shall be : - // - a reference to a function - // - or a reference to a function object([function.objects]) - // - - // If EFP is not an lvalue reference type and is_nothrow_constructible_v is true, - // initialize exit_function with std::forward(f); - // otherwise initialize exit_function with f. - - // scope_fail / scope_exit - // If the initialization of exit_function throws an exception, calls f(). - - // scope_success - // [Note: If initialization of exit_function fails, f() won’t be called. —end note] - + /** The constructor parameter `exit_func` in the following constructors shall be : + * - a reference to a function + * - or a reference to a function object([function.objects]) + * + * + * If EFP is not an lvalue reference type and is_nothrow_constructible_v is true, + * initialize exit_function with std::forward(f); + * otherwise initialize exit_function with f. + * + * scope_fail / scope_exit + * If the initialization of exit_function throws an exception, calls f(). + * + * scope_success + * [Note: If initialization of exit_function fails, f() wont be called. end note] + */ template constexpr scope_guard(EF&& exit_func, CHKR&& invoke_checker) noexcept(std::is_nothrow_constructible_v && From dd21ccdb97250f2752e3d50e551580182a4d3747 Mon Sep 17 00:00:00 2001 From: JeffGarland Date: Mon, 19 May 2025 05:11:31 -0700 Subject: [PATCH 09/12] cleanup pre-check clang-format complaints --- include/beman/scope/scope.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index e352810..2b218e1 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -209,8 +209,8 @@ class [[nodiscard]] scope_guard; * @tparam ConstructionExceptionBehavior callable function that defines the behavior if an exception occurs * on the construction. */ -template class [[nodiscard]] scope_guard { public: @@ -233,7 +233,7 @@ class [[nodiscard]] scope_guard && std::is_nothrow_constructible_v) try - : exit_func{std::forward(exit_func)}, invoke_check_func{std::forward(invoke_checker)} { + : exit_func{std::forward(exit_func)}, invoke_check_func{std::forward(invoke_checker)} { } catch (...) { if constexpr (ConstructionExceptionBehavior == exception_during_construction_behaviour::invoke_exit_func) { exit_func(); @@ -357,7 +357,7 @@ template scope_guard, std::decay_t, ecdb>; template requires(scope_exit_function && (scope_function_invoke_check || std::is_void_v)) @@ -385,7 +385,7 @@ class ReleasableExecuteWhenNoException { return uncaught_on_creation >= std::uncaught_exceptions(); } - void release() { uncaught_on_creation = std::numeric_limits::min(); } + void release() { uncaught_on_creation = std::numeric_limits::min(); } private: int uncaught_on_creation = std::uncaught_exceptions(); From eea8478acb2956f1a204dd77c40f975af926af0f Mon Sep 17 00:00:00 2001 From: Jeff Garland Date: Mon, 19 May 2025 05:15:37 -0700 Subject: [PATCH 10/12] Update include/beman/scope/scope.hpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- include/beman/scope/scope.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 2b218e1..0893dda 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -50,10 +50,10 @@ namespace beman::scope { // todo temporary template using unique_resource = std::experimental::unique_resource; - -// todo temporary -template > -unique_resource, std::decay_t> + +// todo temporary +template > +unique_resource, std::decay_t > make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)))) { return std::experimental::make_unique_resource_checked(std::forward(r), std::forward(invalid), std::forward(d)); From c384e63f8fc2792d509fde2bce32e35f99ec8b59 Mon Sep 17 00:00:00 2001 From: Jeff Garland Date: Mon, 19 May 2025 05:16:11 -0700 Subject: [PATCH 11/12] Update include/beman/scope/scope.hpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- include/beman/scope/scope.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 0893dda..896adf1 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -185,10 +185,7 @@ concept HasStaticRelease = requires { // --- Enum --- -enum class exception_during_construction_behaviour { - dont_invoke_exit_func, - invoke_exit_func -}; +enum class exception_during_construction_behaviour { dont_invoke_exit_func, invoke_exit_func }; //================================================================================================== From dcfb6ea841756d413b3246f451b73c5c3bff0ecf Mon Sep 17 00:00:00 2001 From: JeffGarland Date: Mon, 19 May 2025 05:24:51 -0700 Subject: [PATCH 12/12] snake case names of classes --- include/beman/scope/scope.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 896adf1..27656fe 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -362,7 +362,7 @@ scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ec //================================================================================================== -class Releaser { +class releaser { public: bool operator()() const { return can_invoke; } @@ -374,7 +374,7 @@ class Releaser { //====== -class ReleasableExecuteWhenNoException { +class releaseable_execute_when_no_exception { public: using DontInvokeOnCreationException = void; @@ -390,7 +390,7 @@ class ReleasableExecuteWhenNoException { //====== -class ReleasableExecuteOnlyWhenException { +class releaseable_execute_only_when_exception { public: [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { return uncaught_on_creation < std::uncaught_exceptions(); @@ -407,16 +407,16 @@ class ReleasableExecuteOnlyWhenException { // --- type aliases --- template -using scope_exit = scope_guard; +using scope_exit = scope_guard; template using scope_success = scope_guard; template using scope_fail = scope_guard; } // namespace beman::scope