Created
July 1, 2023 21:58
-
-
Save razielanarki/684be0c8b6aaf4639b04669a6a8f2b8a to your computer and use it in GitHub Desktop.
std::experimental scope guard support, implements fundamental-ts v3 (see N4948: 6.2.1-2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//========================================================================== | |
// nonstd/scope.h: std::experimental scope guard support, | |
// implements fundamental-ts v3 (see N4948: 6.2.1-2) | |
//========================================================================== | |
// (C) 2023 Raziel Anarki | |
//-------------------------------------------------------------------------- | |
#pragma once | |
#include <exception> | |
#include <concepts> | |
#include <utility> | |
//========================================================================== | |
namespace nonstd | |
{ | |
//-------------------------------------------------------------------------- | |
// defines an "acceptable" scope exit callback | |
template<class T> | |
concept exit_callback = requires | |
( | |
std::conditional_t<std::is_lvalue_reference_v<T>, std::remove_reference_t<T>, T> callback | |
) | |
{ | |
{ callback() } -> std::same_as<void>; | |
}; | |
//========================================================================== | |
// implementation details | |
namespace detail | |
{ | |
//-------------------------------------------------------------------------- | |
// defines a scope exit policy | |
template<class T> | |
concept exit_policy = requires ( T policy ) | |
{ | |
{ policy.should_execute() } -> std::same_as<bool>; | |
}; | |
//-------------------------------------------------------------------------- | |
// forwarding helper | |
template<class T> | |
constexpr decltype(auto) forward_if_nothrow_move_constructible( T&& t ) noexcept | |
{ | |
if constexpr( std::is_nothrow_move_constructible_v<T> ) | |
return std::forward<T>( t ); | |
return t; | |
} | |
//-------------------------------------------------------------------------- | |
// default scope exit policy | |
struct scope_exit_policy | |
{ | |
bool should_execute() const noexcept | |
{ | |
return true; | |
} | |
}; | |
//-------------------------------------------------------------------------- | |
// scope fail exit policy | |
struct scope_fail_policy | |
{ | |
int uncaught_on_creation = std::uncaught_exceptions(); | |
bool should_execute() const noexcept | |
{ | |
return ( uncaught_on_creation < std::uncaught_exceptions() ); | |
} | |
}; | |
//-------------------------------------------------------------------------- | |
// scope success exit policy | |
struct scope_success_policy | |
{ | |
int uncaught_on_creation = std::uncaught_exceptions(); | |
bool should_execute() const noexcept | |
{ | |
return !( uncaught_on_creation < std::uncaught_exceptions() ); | |
} | |
}; | |
//========================================================================== | |
// generic scope-guard template | |
template<exit_callback EF, exit_policy EP> | |
class scope_guard : private EP | |
{ | |
using policy = EP; | |
public: // non-throwing forwarding constructor | |
template<exit_callback EFP> | |
explicit scope_guard( EFP&& callback ) | |
noexcept( | |
std::is_nothrow_constructible_v<EF, EFP> | |
or std::is_nothrow_constructible_v<EF, EFP&> | |
) | |
requires( | |
not std::is_same_v<std::remove_cvref_t<EF>, scope_guard> | |
and std::is_constructible_v<EF, EFP> | |
and not std::is_lvalue_reference_v<EFP> | |
and std::is_nothrow_constructible_v<EF, EFP> | |
) | |
: exit_function( std::forward<EFP>( callback ) ) | |
{ | |
} | |
public: // throwing forwarding constructor | |
template<exit_callback EFP> | |
requires( | |
not std::is_same_v<std::remove_cvref_t<EF>, scope_guard> | |
and std::is_constructible_v<EF, EFP> | |
and std::is_lvalue_reference_v<EFP> | |
) | |
explicit scope_guard( EFP&& callback ) | |
try : exit_function( callback ) | |
{ | |
} | |
catch( ... ) | |
{ | |
callback(); | |
throw; | |
} | |
public: // move constructor | |
scope_guard( scope_guard&& rhs ) | |
noexcept( | |
std::is_nothrow_move_constructible_v<EF> | |
or std::is_nothrow_copy_constructible_v<EF> | |
) | |
requires( | |
not std::is_same_v<std::remove_cvref_t<EF>, scope_guard> | |
and ( | |
std::is_nothrow_move_constructible_v<EF> | |
or std::is_copy_constructible_v<EF> | |
) | |
) | |
: policy::policy ( rhs ) | |
, exit_function( forward_if_nothrow_move_constructible( rhs.exit_function ) ) | |
, execute_on_destruction( rhs.execute_on_destruction ) | |
{ | |
rhs.release(); | |
} | |
public: // deleted copy construtor and copy/move assignment | |
scope_guard( const scope_guard& ) = delete; | |
scope_guard& operator=( const scope_guard& ) = delete; | |
scope_guard& operator=( scope_guard&& ) = delete; | |
public: // destructor | |
~scope_guard() const noexcept | |
{ | |
if( !execute_on_destruction ) ) | |
return; | |
if ( policy::should_execute() ) | |
exit_function(); | |
} | |
public: // disable the exit_function on destruction | |
void release() noexcept | |
{ | |
execute_on_destruction = false; | |
} | |
private: | |
bool execute_on_destruction = true; | |
EF exit_function; | |
}; | |
//========================================================================== | |
} // namespace detail | |
//-------------------------------------------------------------------------- | |
// scope_exit + deduction template | |
template<exit_callback EF> | |
class scope_exit | |
: public detail::scope_guard<EF, detail::scope_exit_policy> | |
{ | |
using detail::scope_guard::scope_guard; | |
}; | |
template<exit_callback EF> | |
scope_exit( EF ) -> scope_exit<EF>; | |
//-------------------------------------------------------------------------- | |
// scope fail + deduction template | |
template<exit_callback EF> | |
class scope_fail | |
: public detail::scope_guard<EF, detail::scope_fail_policy> | |
{ | |
using detail::scope_guard::scope_guard; | |
}; | |
template<exit_callback EF> | |
scope_fail( EF ) -> scope_fail<EF>; | |
//-------------------------------------------------------------------------- | |
// scope success + deduction template | |
template<exit_callback EF> | |
class scope_success | |
: public detail::scope_guard<EF, detail::scope_success_policy> | |
{ | |
using detail::scope_guard::scope_guard; | |
}; | |
template<exit_callback EF> | |
scope_success( EF ) -> scope_success<EF>; | |
//-------------------------------------------------------------------------- | |
} // namescpace nonstd | |
//========================================================================== |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment