diff --git a/include/beman/scope/scope.hpp b/include/beman/scope/scope.hpp index 7d33b2c..ecee329 100644 --- a/include/beman/scope/scope.hpp +++ b/include/beman/scope/scope.hpp @@ -21,12 +21,15 @@ namespace beman::scope { +/// Invokes the supplied callable when the enclosing scope is exited. template using scope_exit = std::experimental::scope_exit; +/// Invokes the supplied callable when the enclosing scope is exited because of an exception. template using scope_fail = std::experimental::scope_fail; +/// Invokes the supplied callable when the enclosing scope is exited normally. template using scope_success = std::experimental::scope_success; @@ -47,11 +50,11 @@ using scope_success = std::experimental::scope_success; namespace beman::scope { -// todo temporary +/// Owns a resource and releases it with a callable when the guard is destroyed. template using unique_resource = std::experimental::unique_resource; -// todo temporary +/// Creates a `unique_resource` that is active only when `r` is not equal to `invalid`. template > unique_resource, std::decay_t > make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( @@ -163,21 +166,27 @@ make_unique_resource_checked(R&& r, const S& invalid, D&& d) noexcept(noexcept( //================================================================================================== // --- Concepts --- + +/// Checks whether invoking `F` with `Args...` produces a value convertible to `R`. template concept invocable_return = std::invocable && std::convertible_to, R>; +/// Describes a callable that can be stored and invoked by a scope guard. template concept scope_exit_function = invocable_return && (std::is_nothrow_move_constructible_v || std::is_copy_constructible_v); +/// Checks whether a callable can decide whether a scope guard should invoke its exit function. template concept scope_function_invoke_check = invocable_return; +/// Checks whether `T` provides an instance `release()` member function. template concept HasRelease = requires(T t) { { t.release() } -> std::same_as; }; +/// Checks whether `T` provides a static `release()` member function. template concept HasStaticRelease = requires { { T::release() } -> std::same_as; @@ -185,12 +194,17 @@ concept HasStaticRelease = requires { // --- Enum --- -enum class exception_during_construction_behaviour { dont_invoke_exit_func, invoke_exit_func }; +/// Controls whether the exit function runs if a scope guard constructor throws. +enum class exception_during_construction_behaviour { + /// Do not invoke the exit function when construction fails. + dont_invoke_exit_func, + /// Invoke the exit function when construction fails. + invoke_exit_func +}; //================================================================================================== -// --- `scope_guard` - primary template --- - +/// Generalized scope guard that conditionally invokes a callable at scope exit. template constexpr scope_guard(EF&& exit_func, CHKR&& invoke_checker) noexcept(std::is_nothrow_constructible_v && @@ -240,6 +253,7 @@ class [[nodiscard]] scope_guard explicit constexpr scope_guard(EF&& exit_func) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) @@ -254,6 +268,7 @@ class [[nodiscard]] scope_guard && std::is_nothrow_move_constructible_v) requires(HasRelease || HasStaticRelease) @@ -272,14 +287,17 @@ class [[nodiscard]] scope_guard || HasStaticRelease) @@ -300,11 +318,13 @@ class [[nodiscard]] scope_guard class [[nodiscard]] scope_guard { ScopeExitFunc exit_func; public: + /// Constructs a guard from an exit function. template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) requires(!std::is_same_v, scope_guard>) @@ -320,16 +340,19 @@ class [[nodiscard]] scope_guard class [[nodiscard]] scope_guard { ScopeExitFunc exit_func; public: + /// Constructs a guard from an exit function. template explicit constexpr scope_guard(T&& exit_func) noexcept(std::is_nothrow_constructible_v) requires(!std::is_same_v, scope_guard>) @@ -340,6 +363,7 @@ class [[nodiscard]] scope_guard requires(scope_exit_function && (scope_function_invoke_check)) scope_guard(ExitFunc&&, InvokeChecker&&) -> scope_guard, std::decay_t, ecdb>; +/// Deduction guide for a guard with a default invocation checker. template @@ -362,10 +388,13 @@ scope_guard(ExitFunc&&) -> scope_guard, InvokeChecker, ec //================================================================================================== +/// Invocation checker used by `scope_exit`. class releaser { public: + /// Returns whether the exit function is still enabled. bool operator()() const { return can_invoke; } + /// Disables the exit function. void release() { can_invoke = false; } private: @@ -374,14 +403,18 @@ class releaser { //====== +/// Invocation checker that enables execution only when no exception is active. class releaseable_execute_when_no_exception { public: + /// Marker used to select the construction behavior for `scope_success`. using DontInvokeOnCreationException = void; + /// Returns whether destruction is occurring without a new exception. [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { return uncaught_on_creation >= std::uncaught_exceptions(); } + /// Disables the exit function. void release() { uncaught_on_creation = std::numeric_limits::min(); } private: @@ -390,12 +423,15 @@ class releaseable_execute_when_no_exception { //====== +/// Invocation checker that enables execution only while unwinding an exception. class releaseable_execute_only_when_exception { public: + /// Returns whether destruction is occurring during exception unwinding. [[nodiscard]] bool operator()() const noexcept(noexcept(std::uncaught_exceptions())) { return uncaught_on_creation < std::uncaught_exceptions(); } + /// Disables the exit function. void release() { uncaught_on_creation = std::numeric_limits::max(); } private: @@ -406,14 +442,17 @@ class releaseable_execute_only_when_exception { // --- type aliases --- +/// Executes a callable on every scope exit unless released. template using scope_exit = scope_guard; +/// Executes a callable only when the scope exits without an exception. template using scope_success = scope_guard; +/// Executes a callable only when the scope exits during exception unwinding. template using scope_fail = scope_guard