Skip to content

Instantly share code, notes, and snippets.

@razielanarki
Created July 1, 2023 21:58
Show Gist options
  • Save razielanarki/684be0c8b6aaf4639b04669a6a8f2b8a to your computer and use it in GitHub Desktop.
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)
//==========================================================================
// 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