blob: 03699d3b4aa6e220622e71ddb432b057c685927d [file] [log] [blame]
//
// Copyright (c) 2020-2020 Martin Moene
//
// https://github.com/martinmoene/scope-lite
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// C++ standard libraries extensions, version 3
// https://en.cppreference.com/w/cpp/experimental/lib_extensions_3
#ifndef NONSTD_SCOPE_LITE_HPP
#define NONSTD_SCOPE_LITE_HPP
#define scope_lite_MAJOR 0
#define scope_lite_MINOR 1
#define scope_lite_PATCH 0
#define scope_lite_VERSION scope_STRINGIFY(scope_lite_MAJOR) "." scope_STRINGIFY(scope_lite_MINOR) "." scope_STRINGIFY(scope_lite_PATCH)
#define scope_STRINGIFY( x ) scope_STRINGIFY_( x )
#define scope_STRINGIFY_( x ) #x
// scope-lite configuration:
#define scope_SCOPE_DEFAULT 0
#define scope_SCOPE_NONSTD 1
#define scope_SCOPE_STD 2
#if !defined( scope_CONFIG_SELECT_SCOPE )
# define scope_CONFIG_SELECT_SCOPE ( scope_HAVE_STD_SCOPE ? scope_SCOPE_STD : scope_SCOPE_NONSTD )
#endif
// #if !defined( scope_CONFIG_STRICT )
// # define scope_CONFIG_STRICT 0
// #endif
// C++ language version detection (C++20 is speculative):
// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
#ifndef scope_CPLUSPLUS
# if defined(_MSVC_LANG ) && !defined(__clang__)
# define scope_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
# else
# define scope_CPLUSPLUS __cplusplus
# endif
#endif
#define scope_CPP98_OR_GREATER ( scope_CPLUSPLUS >= 199711L )
#define scope_CPP11_OR_GREATER ( scope_CPLUSPLUS >= 201103L )
#define scope_CPP14_OR_GREATER ( scope_CPLUSPLUS >= 201402L )
#define scope_CPP17_OR_GREATER ( scope_CPLUSPLUS >= 201703L )
#define scope_CPP20_OR_GREATER ( scope_CPLUSPLUS >= 202000L )
// Use C++yy <scope> if available and requested:
#if scope_CPP20_OR_GREATER && defined(__has_include )
# if __has_include( <scope> )
# define scope_HAVE_STD_SCOPE 1
# else
# define scope_HAVE_STD_SCOPE 0
# endif
#else
# define scope_HAVE_STD_SCOPE 0
#endif
#define scope_USES_STD_SCOPE ( (scope_CONFIG_SELECT_SCOPE == scope_SCOPE_STD) || ((scope_CONFIG_SELECT_SCOPE == scope_SCOPE_DEFAULT) && scope_HAVE_STD_SCOPE) )
//
// Using std <scope>:
//
#if scope_USES_STD_SCOPE
#include <scope>
namespace nonstd
{
using std::scope_exit;
using std::scope_fail;
using std::scope_success;
using std::unique_resource;
using std::make_scope_exit;
using std::make_scope_fail;
using std::make_scope_success;
using std::make_unique_resource_checked;
}
#else // scope_USES_STD_SCOPE
// half-open range [lo..hi):
#define scope_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
// Compiler versions:
//
// MSVC++ 6.0 _MSC_VER == 1200 scope_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0)
// MSVC++ 7.0 _MSC_VER == 1300 scope_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002)
// MSVC++ 7.1 _MSC_VER == 1310 scope_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003)
// MSVC++ 8.0 _MSC_VER == 1400 scope_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005)
// MSVC++ 9.0 _MSC_VER == 1500 scope_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008)
// MSVC++ 10.0 _MSC_VER == 1600 scope_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010)
// MSVC++ 11.0 _MSC_VER == 1700 scope_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012)
// MSVC++ 12.0 _MSC_VER == 1800 scope_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013)
// MSVC++ 14.0 _MSC_VER == 1900 scope_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015)
// MSVC++ 14.1 _MSC_VER >= 1910 scope_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017)
// MSVC++ 14.2 _MSC_VER >= 1920 scope_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019)
#if defined(_MSC_VER ) && !defined(__clang__)
# define scope_COMPILER_MSVC_VER (_MSC_VER )
# define scope_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
#else
# define scope_COMPILER_MSVC_VER 0
# define scope_COMPILER_MSVC_VERSION 0
#endif
// Courtesy of https://github.com/gsl-lite/gsl-lite
// AppleClang 7.0.0 __apple_build_version__ == 7000172 scope_COMPILER_APPLECLANG_VERSION == 700 (Xcode 7.0, 7.0.1) (LLVM 3.7.0)
// AppleClang 7.0.0 __apple_build_version__ == 7000176 scope_COMPILER_APPLECLANG_VERSION == 700 (Xcode 7.1) (LLVM 3.7.0)
// AppleClang 7.0.2 __apple_build_version__ == 7000181 scope_COMPILER_APPLECLANG_VERSION == 702 (Xcode 7.2, 7.2.1) (LLVM 3.7.0)
// AppleClang 7.3.0 __apple_build_version__ == 7030029 scope_COMPILER_APPLECLANG_VERSION == 730 (Xcode 7.3) (LLVM 3.8.0)
// AppleClang 7.3.0 __apple_build_version__ == 7030031 scope_COMPILER_APPLECLANG_VERSION == 730 (Xcode 7.3.1) (LLVM 3.8.0)
// AppleClang 8.0.0 __apple_build_version__ == 8000038 scope_COMPILER_APPLECLANG_VERSION == 800 (Xcode 8.0) (LLVM 3.9.0)
// AppleClang 8.0.0 __apple_build_version__ == 8000042 scope_COMPILER_APPLECLANG_VERSION == 800 (Xcode 8.1, 8.2, 8.2.1) (LLVM 3.9.0)
// AppleClang 8.1.0 __apple_build_version__ == 8020038 scope_COMPILER_APPLECLANG_VERSION == 810 (Xcode 8.3) (LLVM 3.9.0)
// AppleClang 8.1.0 __apple_build_version__ == 8020041 scope_COMPILER_APPLECLANG_VERSION == 810 (Xcode 8.3.1) (LLVM 3.9.0)
// AppleClang 8.1.0 __apple_build_version__ == 8020042 scope_COMPILER_APPLECLANG_VERSION == 810 (Xcode 8.3.2, 8.3.3) (LLVM 3.9.0)
// AppleClang 9.0.0 __apple_build_version__ == 9000037 scope_COMPILER_APPLECLANG_VERSION == 900 (Xcode 9.0) (LLVM 4.0.0?)
// AppleClang 9.0.0 __apple_build_version__ == 9000038 scope_COMPILER_APPLECLANG_VERSION == 900 (Xcode 9.1) (LLVM 4.0.0?)
// AppleClang 9.0.0 __apple_build_version__ == 9000039 scope_COMPILER_APPLECLANG_VERSION == 900 (Xcode 9.2) (LLVM 4.0.0?)
// AppleClang 9.1.0 __apple_build_version__ == 9020039 scope_COMPILER_APPLECLANG_VERSION == 910 (Xcode 9.3, 9.3.1) (LLVM 5.0.2?)
// AppleClang 9.1.0 __apple_build_version__ == 9020039 scope_COMPILER_APPLECLANG_VERSION == 910 (Xcode 9.4, 9.4.1) (LLVM 5.0.2?)
// AppleClang 10.0.0 __apple_build_version__ == 10001145 scope_COMPILER_APPLECLANG_VERSION == 1000 (Xcode 10.0, 10.1) (LLVM 6.0.1?)
// AppleClang 10.0.1 __apple_build_version__ == 10010046 scope_COMPILER_APPLECLANG_VERSION == 1001 (Xcode 10.2, 10.2.1, 10.3) (LLVM 7.0.0?)
// AppleClang 11.0.0 __apple_build_version__ == 11000033 scope_COMPILER_APPLECLANG_VERSION == 1100 (Xcode 11.1, 11.2, 11.3) (LLVM 8.0.0?)
#define scope_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
#if defined( __apple_build_version__ )
# define scope_COMPILER_APPLECLANG_VERSION scope_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ )
# define scope_COMPILER_CLANG_VERSION 0
#elif defined( __clang__ )
# define scope_COMPILER_APPLECLANG_VERSION 0
# define scope_COMPILER_CLANG_VERSION scope_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ )
#else
# define scope_COMPILER_APPLECLANG_VERSION 0
# define scope_COMPILER_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define scope_COMPILER_GNUC_VERSION scope_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#else
# define scope_COMPILER_GNUC_VERSION 0
#endif
// Presence of language and library features:
#define scope_HAVE( feature ) ( scope_HAVE_##feature )
#ifdef _HAS_CPP0X
# define scope_HAS_CPP0X _HAS_CPP0X
#else
# define scope_HAS_CPP0X 0
#endif
#define scope_CPP11_90 (scope_CPP11_OR_GREATER || scope_COMPILER_MSVC_VER >= 1500)
#define scope_CPP11_100 (scope_CPP11_OR_GREATER || scope_COMPILER_MSVC_VER >= 1600)
#define scope_CPP11_110 (scope_CPP11_OR_GREATER || scope_COMPILER_MSVC_VER >= 1700)
#define scope_CPP11_120 (scope_CPP11_OR_GREATER || scope_COMPILER_MSVC_VER >= 1800)
#define scope_CPP11_140 (scope_CPP11_OR_GREATER || scope_COMPILER_MSVC_VER >= 1900)
#define scope_CPP14_000 (scope_CPP14_OR_GREATER)
#define scope_CPP17_000 (scope_CPP17_OR_GREATER)
#define scope_CPP17_140 (scope_CPP17_OR_GREATER || scope_COMPILER_MSVC_VER >= 1900)
// Presence of C++11 language features:
#define scope_HAVE_CONSTEXPR_11 scope_CPP11_140
// #define scope_HAVE_ENUM_CLASS scope_CPP11_110
#define scope_HAVE_IS_DEFAULT scope_CPP11_120
#define scope_HAVE_IS_DELETE scope_CPP11_120
#define scope_HAVE_NOEXCEPT scope_CPP11_140
#define scope_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG scope_CPP11_120
#define scope_HAVE_STATIC_ASSERT scope_CPP11_90
#define scope_HAVE_TRAILING_RETURN_TYPE scope_CPP11_120
#define scope_HAVE_VALUE_INITIALIZATION scope_CPP11_120
// Presence of C++14 language features:
#define scope_HAVE_CONSTEXPR_14 scope_CPP14_000
// Presence of C++17 language features:
#define scope_HAVE_DEDUCTION_GUIDES scope_CPP17_000
#define scope_HAVE_NODISCARD scope_CPP17_000
// Presence of C++11 library features:
#define scope_HAVE_IS_TRIVIAL scope_CPP11_110
#define scope_HAVE_IS_TRIVIALLY_COPYABLE scope_CPP11_110 && !scope_BETWEEN(scope_COMPILER_GNUC_VERSION, 1, 500) // GCC >= 5
#define scope_HAVE_IS_CONSTRUCTIBLE scope_CPP11_110
#define scope_HAVE_IS_COPY_CONSTRUCTIBLE scope_CPP11_110
#define scope_HAVE_IS_MOVE_CONSTRUCTIBLE scope_CPP11_110
#define scope_HAVE_IS_NOTHROW_CONSTRUCTIBLE scope_CPP11_110
#define scope_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE scope_CPP11_110
#define scope_HAVE_IS_COPY_ASSIGNABLE scope_CPP11_110
#define scope_HAVE_IS_NOTHROW_ASSIGNABLE scope_CPP11_110
#define scope_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE scope_CPP11_110
#define scope_HAVE_REMOVE_CV scope_CPP11_90
#define scope_HAVE_REMOVE_REFERENCE scope_CPP11_90
#define scope_HAVE_TYPE_TRAITS scope_CPP11_110
#define scope_HAVE_TR1_TYPE_TRAITS ((!! scope_COMPILER_GNUC_VERSION ) && scope_CPP11_OR_GREATER)
#define scope_HAVE_DECAY scope_CPP11_110
#define scope_HAVE_DECAY_TR1 scope_HAVE_TR1_TYPE_TRAITS
#define scope_HAVE_IS_SAME scope_HAVE_TYPE_TRAITS
#define scope_HAVE_IS_SAME_TR1 scope_HAVE_TR1_TYPE_TRAITS
// #define scope_HAVE_CSTDINT scope_CPP11_90
// Presence of C++14 library features:
// Presence of C++17 library features:
#define scope_HAVE_UNCAUGHT_EXCEPTIONS scope_CPP17_140
// Presence of C++ language features:
#if scope_HAVE_CONSTEXPR_11
# define scope_constexpr constexpr
#else
# define scope_constexpr /*constexpr*/
#endif
#if scope_HAVE_CONSTEXPR_14
# define scope_constexpr14 constexpr
#else
# define scope_constexpr14 /*constexpr*/
#endif
#if scope_HAVE( IS_DELETE )
# define scope_is_delete = delete
# define scope_is_delete_access public
#else
# define scope_is_delete
# define scope_is_delete_access private
#endif
#if scope_HAVE_NOEXCEPT
# define scope_noexcept noexcept
# define scope_noexcept_op(expr) noexcept(expr)
#else
# define scope_noexcept /*noexcept*/
# define scope_noexcept_op(expr) /*noexcept(expr)*/
#endif
#if scope_HAVE_NODISCARD
# define scope_nodiscard [[nodiscard]]
#else
# define scope_nodiscard /*[[nodiscard]]*/
#endif
#if scope_HAVE_STATIC_ASSERT
# define scope_static_assert(expr, msg) static_assert((expr), msg)
#else
# define scope_static_assert(expr, msg) /*static_assert((expr), msg)*/
#endif
// Select C++98 version
#define scope_USE_POST_CPP98_VERSION scope_CPP11_100
// Additional includes:
#include <exception> // exception, terminate(), uncaught_exceptions()
#include <limits> // std::numeric_limits<>
#include <utility> // move(), forward<>(), swap()
#if scope_HAVE_TYPE_TRAITS
# include <type_traits>
#elif scope_HAVE_TR1_TYPE_TRAITS
# include <tr1/type_traits>
#endif
// Method enabling (return type):
#if scope_HAVE( TYPE_TRAITS )
# define scope_ENABLE_IF_R_(VA, R) typename std::enable_if< (VA), R >::type
#else
# define scope_ENABLE_IF_R_(VA, R) R
#endif
// Method enabling (funtion template argument):
#if scope_HAVE( TYPE_TRAITS ) && scope_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
// VS 2013 seems to have trouble with SFINAE for default non-type arguments:
# if !scope_BETWEEN( scope_COMPILER_MSVC_VERSION, 1, 140 )
# define scope_ENABLE_IF_(VA) , typename std::enable_if< ( VA ), int >::type = 0
# else
# define scope_ENABLE_IF_(VA) , typename = typename std::enable_if< ( VA ), ::nonstd::scope::enabler >::type
# endif
#else
# define scope_ENABLE_IF_(VA)
#endif
// Declare __cxa_get_globals() or equivalent in namespace nonstd::scope for uncaught_exceptions():
#if !scope_HAVE( UNCAUGHT_EXCEPTIONS )
# if scope_COMPILER_MSVC_VERSION // libstl :)
namespace nonstd { namespace scope { extern "C" char * __cdecl _getptd(); }}
# elif scope_COMPILER_CLANG_VERSION || scope_COMPILER_GNUC_VERSION || scope_COMPILER_APPLECLANG_VERSION
# if defined(__GLIBCXX__) || defined(__GLIBCPP__) // libstdc++: prototype from cxxabi.h
# include <cxxabi.h>
# elif !defined(BOOST_CORE_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED_) // libc++: prototype from Boost?
# if defined(__FreeBSD__) || defined(__OpenBSD__)
namespace __cxxabiv1 { struct __cxa_eh_globals; extern "C" __cxa_eh_globals * __cxa_get_globals(); }
# else
namespace __cxxabiv1 { struct __cxa_eh_globals; extern "C" __cxa_eh_globals * __cxa_get_globals() scope_noexcept; }
# endif
# endif
namespace nonstd { namespace scope { using ::__cxxabiv1::__cxa_get_globals; }}
# endif // scope_COMPILER_MSVC_VERSION
#endif // !scope_HAVE( UNCAUGHT_EXCEPTIONS )
// Namespace nonstd:
namespace nonstd {
namespace scope {
// for scope_ENABLE_IF_():
/*enum*/ class enabler{};
// C++11 emulation:
namespace std11 {
template< class T, T v > struct integral_constant { enum { value = v }; };
template< bool B > struct bool_constant : integral_constant<bool, B>{};
typedef bool_constant< true > true_type;
typedef bool_constant< false > false_type;
template <class T> struct is_reference : false_type{};
template <class T> struct is_reference<T&> : true_type {};
#if scope_CPP11_100
template <class T> struct is_reference<T&&> : true_type {};
#endif
template< class T > struct remove_pointer { typedef T type; };
template< class T > struct remove_pointer<T*> { typedef T type; };
template< class T > struct remove_pointer<T* const> { typedef T type; };
template< class T > struct remove_pointer<T* volatile> { typedef T type; };
template< class T > struct remove_pointer<T* const volatile> { typedef T type; };
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
#if scope_HAVE( DECAY )
using std::decay;
#elif scope_HAVE( DECAY_TR1 )
using std::tr1::decay;
#else
template< class T > struct decay{ typedef T type; };
#endif
#if scope_HAVE( IS_TRIVIAL )
using std::is_trivial;
#else
template< class T > struct is_trivial : std11::true_type{};
#endif
#if scope_HAVE( IS_TRIVIALLY_COPYABLE )
using std::is_trivially_copyable;
#else
template< class T > struct is_trivially_copyable : std11::true_type{};
#endif
#if scope_HAVE( IS_CONSTRUCTIBLE )
using std::is_constructible;
#else
template< class T > struct is_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_COPY_CONSTRUCTIBLE )
using std::is_copy_constructible;
#else
template< class T > struct is_copy_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_MOVE_CONSTRUCTIBLE )
using std::is_move_constructible;
#else
template< class T > struct is_move_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_NOTHROW_CONSTRUCTIBLE )
using std::is_nothrow_constructible;
#else
template< class T, class U > struct is_nothrow_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_NOTHROW_COPY_CONSTRUCTIBLE )
using std::is_nothrow_copy_constructible;
#else
template< class T > struct is_nothrow_copy_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_NOTHROW_MOVE_CONSTRUCTIBLE )
using std::is_nothrow_move_constructible;
#else
template< class T > struct is_nothrow_move_constructible : std11::true_type{};
#endif
#if scope_HAVE( IS_COPY_ASSIGNABLE )
using std::is_copy_assignable;
#else
template< class T > struct is_copy_assignable : std11::true_type{};
#endif
#if scope_HAVE( IS_NOTHROW_ASSIGNABLE )
using std::is_nothrow_assignable;
#else
template< class T, class U > struct is_nothrow_assignable : std11::true_type{};
#endif
#if scope_HAVE( IS_NOTHROW_MOVE_ASSIGNABLE )
using std::is_nothrow_move_assignable;
#else
template< class T > struct is_nothrow_move_assignable : std11::true_type{};
#endif
#if scope_HAVE( IS_SAME )
using std::is_same;
#elif scope_HAVE( IS_SAME_TR1 )
using std::tr1::is_same;
#else
template< class T, class U > struct is_same : std11::true_type{};
#endif
#if scope_HAVE( REMOVE_CV )
using std::remove_cv;
#else
template< class T > struct remove_cv{ typedef T type; };
#endif
#if scope_HAVE( REMOVE_REFERENCE )
using std::remove_reference;
#else
template< class T > struct remove_reference{ typedef T type; };
#endif
#if scope_HAVE( REFERENCE_WRAPPER )
using std::reference_wrapper;
#else
template< class T > struct reference_wrapper{ typedef T type; };
#endif
} // namepsace std11
// C++14 emulation:
namespace std14 {
#if scope_CPP11_100
#if scope_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
template< class T, class U = T >
#else
template< class T, class U /*= T*/ >
#endif
scope_constexpr14 T exchange( T & obj, U && new_value )
{
T old_value = std::move( obj );
obj = std::forward<U>( new_value );
return old_value;
}
#else
// C++98 version?
#endif
} // namespace std14
// C++17 emulation (uncaught_exceptions):
namespace std17 {
template< typename T >
inline int to_int( T x ) scope_noexcept
{
return static_cast<int>( x );
}
#if scope_HAVE( UNCAUGHT_EXCEPTIONS )
inline int uncaught_exceptions() scope_noexcept
{
return to_int( std::uncaught_exceptions() );
}
#elif scope_COMPILER_MSVC_VERSION
inline int uncaught_exceptions() scope_noexcept
{
return to_int( *reinterpret_cast<const unsigned*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90) ) );
}
#elif scope_COMPILER_CLANG_VERSION || scope_COMPILER_GNUC_VERSION || scope_COMPILER_APPLECLANG_VERSION
inline int uncaught_exceptions() scope_noexcept
{
return to_int( *reinterpret_cast<const unsigned*>(
reinterpret_cast<const unsigned char*>(__cxa_get_globals()) + sizeof(void*) ) );
}
#endif // scope_HAVE( UNCAUGHT_EXCEPTIONS )
} // namespace std17
// C++20 emulation:
namespace std20 {
template< class T >
struct remove_cvref
{
typedef typename std11::remove_cv<typename std11::remove_reference<T>::type>::type type;
};
template< class T, class U >
struct same_as : std11::integral_constant<bool, std11::is_same<T,U>::value && std11::is_same<U,T>::value> {};
template< class T >
struct type_identity { typedef T type; };
} // namepsace std20
//
// For reference:
//
#if 0
template< class EF>
class scope_exit;
template< class EF>
class scope_fail;
template< class EF>
class scope_success;
template<class R,class D>
class unique_resource;
// special factory function:
template<class R,class D, class S=R>
unique_resource<decay_t<R>, decay_t<D>>
make_unique_resource_checked(R&& r, S const& invalid, D&& d)
noexcept(is_nothrow_constructible_v<decay_t<R>, R> && is_nothrow_constructible_v<decay_t<D>, D>);
// optional factory functions (should at least be present for LFTS3):
template< class EF>
scope_exit<decay_t<EF>>
make_scope_exit(EF&& exit_function) ;
template< class EF>
scope_fail<decay_t<EF>>
make_scope_fail(EF&& exit_function) ;
template< class EF>
scope_success<decay_t<EF>>
make_scope_success(EF&& exit_function) ;
#endif // reference
#if scope_USE_POST_CPP98_VERSION
//
// Post-C++98 version:
//
template< typename T >
T && conditional_forward( T && t, std11::true_type )
{
return std::forward<T>( t );
}
template< typename T >
T const & conditional_forward( T && t, std11::false_type )
{
return t;
}
template< typename T >
T && conditional_move( T && t, std11::true_type )
{
return std::move( t );
}
template< typename T >
T const & conditional_move( T && t, std11::false_type )
{
return t;
}
// template< typename FE, typename Fn >
// struct to_argument_type<EF,Fn>
// {
// };
// scope_exit:
template< class EF >
class scope_exit
{
public:
template< class Fn
scope_ENABLE_IF_((
!std11::is_same<typename std20::remove_cvref<Fn>::type, scope_exit>::value
&& std11::is_constructible<EF, Fn>::value
))
>
explicit scope_exit( Fn&& fn )
scope_noexcept_op
((
std11::is_nothrow_constructible<EF, Fn>::value
|| std11::is_nothrow_constructible<EF, Fn&>::value
))
: exit_function(
// to_argument_type<EF,Fn>( std::forward<Fn>(fn) ) )
conditional_forward<Fn>( std::forward<Fn>(fn)
, std11::bool_constant< std11::is_nothrow_constructible<EF, Fn>::value >() ) )
, execute_on_destruction( true )
{}
scope_exit( scope_exit && other )
scope_noexcept_op
((
std11::is_nothrow_move_constructible<EF>::value
|| std11::is_nothrow_copy_constructible<EF>::value
))
: exit_function( std::forward<EF>( other.exit_function ) )
, execute_on_destruction( other.execute_on_destruction )
{
other.release();
}
~scope_exit() scope_noexcept
{
if ( execute_on_destruction )
exit_function();
}
void release() scope_noexcept
{
execute_on_destruction = false;
}
scope_is_delete_access:
scope_exit( scope_exit const & ) scope_is_delete;
scope_exit & operator=( scope_exit const & ) scope_is_delete;
scope_exit & operator=( scope_exit && ) scope_is_delete;
private:
EF exit_function;
bool execute_on_destruction; // { true };
};
// scope_fail:
template< class EF >
class scope_fail
{
public:
template< class Fn
scope_ENABLE_IF_((
!std11::is_same<typename std20::remove_cvref<Fn>::type, scope_fail>::value
&& std11::is_constructible<EF, Fn>::value
))
>
explicit scope_fail( Fn&& fn )
scope_noexcept_op
((
std11::is_nothrow_constructible<EF, Fn>::value
|| std11::is_nothrow_constructible<EF, Fn&>::value
))
: exit_function(
conditional_forward<Fn>( std::forward<Fn>(fn)
, std11::bool_constant< std11::is_nothrow_constructible<EF, Fn>::value >() ) )
, uncaught_on_creation( std17::uncaught_exceptions() )
{}
scope_fail( scope_fail && other )
scope_noexcept_op
((
std11::is_nothrow_move_constructible<EF>::value
|| std11::is_nothrow_copy_constructible<EF>::value
))
: exit_function( std::forward<EF>( other.exit_function ) )
, uncaught_on_creation( other.uncaught_on_creation )
{
other.release();
}
~scope_fail() scope_noexcept
{
if ( uncaught_on_creation < std17::uncaught_exceptions() )
exit_function();
}
void release() scope_noexcept
{
uncaught_on_creation = std::numeric_limits<int>::max();
}
scope_is_delete_access:
scope_fail( scope_fail const & ) scope_is_delete;
scope_fail & operator=( scope_fail const & ) scope_is_delete;
scope_fail & operator=( scope_fail && ) scope_is_delete;
private:
EF exit_function;
int uncaught_on_creation; // { std17::uncaught_exceptions() };
};
// scope_success:
template< class EF >
class scope_success
{
public:
template< class Fn
scope_ENABLE_IF_((
!std11::is_same<typename std20::remove_cvref<Fn>::type, scope_success>::value
&& std11::is_constructible<EF, Fn>::value
))
>
explicit scope_success( Fn&& fn )
scope_noexcept_op
((
std11::is_nothrow_constructible<EF, Fn>::value
|| std11::is_nothrow_constructible<EF, Fn&>::value
))
: exit_function(
conditional_forward<Fn>( std::forward<Fn>(fn)
, std11::bool_constant< std11::is_nothrow_constructible<EF, Fn>::value >() ) )
, uncaught_on_creation( std17::uncaught_exceptions() )
{}
scope_success( scope_success && other )
scope_noexcept_op
((
std11::is_nothrow_move_constructible<EF>::value
|| std11::is_nothrow_copy_constructible<EF>::value
))
: exit_function( std::forward<EF>( other.exit_function ) )
, uncaught_on_creation( other.uncaught_on_creation )
{
other.release();
}
~scope_success() scope_noexcept
{
if ( uncaught_on_creation >= std17::uncaught_exceptions() )
exit_function();
}
void release() scope_noexcept
{
uncaught_on_creation = -1;
}
scope_is_delete_access:
scope_success( scope_success const & ) scope_is_delete;
scope_success & operator=( scope_success const & ) scope_is_delete;
scope_success & operator=( scope_success && ) scope_is_delete;
private:
EF exit_function;
int uncaught_on_creation; // { std17::uncaught_exceptions() };
};
#if scope_HAVE( DEDUCTION_GUIDES )
template< class EF > scope_exit(EF) -> scope_exit<EF>;
template< class EF > scope_fail(EF) -> scope_fail<EF>;
template< class EF > scope_success(EF) -> scope_success<EF>;
#endif
// optional factory functions (should at least be present for LFTS3):
template< class EF >
scope_exit<typename std11::decay<EF>::type>
make_scope_exit( EF && exit_function )
{
return scope_exit<typename std11::decay<EF>::type>( std::forward<EF>( exit_function ) );
}
template< class EF >
scope_fail<typename std11::decay<EF>::type>
make_scope_fail( EF && exit_function )
{
return scope_fail<typename std11::decay<EF>::type>( std::forward<EF>( exit_function ) );
}
template< class EF >
scope_success<typename std11::decay<EF>::type>
make_scope_success( EF && exit_function )
{
return scope_success<typename std11::decay<EF>::type>( std::forward<EF>( exit_function ) );
}
// unique_resource:
template< class R, class D >
class unique_resource
{
private:
scope_static_assert(
( std11::is_move_constructible<R>::value && std11::is_nothrow_move_constructible<R>::value )
|| std11::is_copy_constructible<R>::value
, "resource must be nothrow_move_constructible or copy_constructible"
);
scope_static_assert(
(std11::is_move_constructible<R>::value && std11::is_nothrow_move_constructible<D>::value )
|| std11::is_copy_constructible<D>::value
, "deleter must be nothrow_move_constructible or copy_constructible"
);
typedef typename std11::conditional<
std11::is_reference<R>::value
, typename std11::reference_wrapper< typename std11::remove_reference<R>::type >::type
, R
>::type R1;
public:
// This overload only participates in overload resolution if:
// - std::is_default_constructible_v<R>
// - && std::is_default_constructible_v<D>
unique_resource()
#if scope_HAVE( VALUE_INITIALIZATION )
: resource{}
, deleter{}
, execute_on_reset{ false }
#else
: resource()
, deleter()
, execute_on_reset( false )
#endif
{}
// construction: note extra execute default parameter
template< class RR, class DD
#if scope_BETWEEN( scope_COMPILER_MSVC_VERSION, 120, 130 )
scope_ENABLE_IF_(( true
// && std11::is_constructible<R1, RR>::value
&& std11::is_constructible<D , DD>::value
// && (std11::is_nothrow_constructible<R1, RR>::value || std11::is_constructible<R1, RR&>::value )
&& std11::is_nothrow_constructible<D, DD>::value || std11::is_constructible<D, DD&>::value
))
#else
scope_ENABLE_IF_(( true
&& std11::is_constructible<R1, RR>::value
&& std11::is_constructible<D , DD>::value
&& (std11::is_nothrow_constructible<R1, RR>::value || std11::is_constructible<R1, RR&>::value )
&& std11::is_nothrow_constructible<D, DD>::value || std11::is_constructible<D, DD&>::value
))
#endif
>
unique_resource( RR && r, DD && d, bool execute = true )
scope_noexcept_op((
( std11::is_nothrow_constructible<R1, RR>::value || std11::is_nothrow_constructible<R1, RR&>::value )
&& ( std11::is_nothrow_constructible<D, DD>::value || std11::is_nothrow_constructible<D, DD&>::value )
))
: resource( conditional_forward<RR>( std::forward<RR>(r)
, std11::bool_constant< std11::is_nothrow_constructible<R1, RR>::value >() ) )
, deleter( ( conditional_forward<DD>( std::forward<DD>(d)
, std11::bool_constant< std11::is_nothrow_constructible<D, DD>::value >() ) ) )
, execute_on_reset( execute )
{}
// Move constructor.
//
// The stored resource handle is initialized from the one of other, using std::move if
// std::is_nothrow_move_constructible_v<RS> is true.
//
// If initialization of the stored resource handle throws an exception, other is not modified.
//
// Then, the deleter is initialized with the one of other, using std::move if
// std::is_nothrow_move_constructible_v<D> is true.
//
// If initialization of the deleter throws an exception and std::is_nothrow_move_constructible_v<RS> is true and
// other owns the resource, calls the deleter of other with res_ to dispose the resource, then calls other.release().
//
// After construction, the constructed unique_resource owns its resource if and only if other owned the resource before
// the construction, and other is set to not own the resource.
unique_resource( unique_resource && other )
scope_noexcept_op(
std11::is_nothrow_move_constructible<R1>::value && std11::is_nothrow_move_constructible<D>::value
)
try
: resource( conditional_move( std::move(other.resource), typename std11::bool_constant< std11::is_nothrow_move_assignable<R>::value >() ) )
, deleter( conditional_move( std::move(other.deleter ), typename std11::bool_constant< std11::is_nothrow_move_constructible<D>::value >() ) )
, execute_on_reset( std14::exchange( other.execute_on_reset, false ) )
{}
catch(...)
{
if ( other.execute_on_reset && std11::is_nothrow_move_constructible<R>::value )
{
other.get_deleter()( this->get() );
other.release();
}
}
~unique_resource()
{
reset();
}
private:
// assign_rd( r, is_nothrow_move_assignable_v<R>, is_nothrow_move_assignable_v<D> ):
void assign_rd( unique_resource && other, std11::true_type, std11::true_type )
{
resource = std::move( other.resource );
deleter = std::move( other.deleter );
}
void assign_rd( unique_resource && other, std11::true_type, std11::false_type )
{
resource = std::move( other.resource );
deleter = other.deleter;
}
void assign_rd( unique_resource && other, std11::false_type, std11::true_type )
{
deleter = std::move( other.deleter );
resource = other.resource;
}
void assign_rd( unique_resource && other, std11::false_type, std11::false_type )
{
resource = other.resource;
deleter = other.deleter;
}
public:
unique_resource & operator=( unique_resource && other )
scope_noexcept_op(
std11::is_nothrow_move_assignable<R1>::value && std11::is_nothrow_move_assignable<D>::value
)
{
scope_static_assert(
std11::is_nothrow_move_assignable<R>::value || std11::is_copy_assignable<R>::value
, "The resource must be nothrow-move assignable, or copy assignable"
);
scope_static_assert(
std11::is_nothrow_move_assignable<D>::value || std11::is_copy_assignable<D>::value
, "The deleter must be nothrow-move assignable, or copy assignable");
if ( &other != this )
{
reset();
assign_rd(
std::move( other )
, typename std11::bool_constant< std11::is_nothrow_move_assignable<R>::value >()
, typename std11::bool_constant< std11::is_nothrow_move_assignable<D>::value >()
);
execute_on_reset = std14::exchange( other.execute_on_reset, false );
}
return *this;
}
void reset() scope_noexcept
{
if ( execute_on_reset )
{
execute_on_reset = false;
get_deleter()( get() );
}
}
template< class RR >
void reset( RR && r )
#if scope_CPP11_110
{
auto && guard = make_scope_fail( [&, this]{ get_deleter()(r); } ); // -Wunused-variable on clang
reset();
resource = conditional_forward<RR>( std::forward<RR>(r)
, std11::bool_constant< std11::is_nothrow_assignable<R1, RR>::value >() );
execute_on_reset = true;
}
#else // scope_CPP11_110
try
{
reset();
resource = conditional_forward<RR>( std::forward<RR>(r)
, std11::bool_constant< std11::is_nothrow_assignable<R1, RR>::value >() );
execute_on_reset = true;
}
catch(...)
{
this->get_deleter()(r);
}
#endif // scope_CPP11_110
void release() scope_noexcept
{
execute_on_reset = false;
}
R1 const & get() const scope_noexcept
{
return resource;
}
// VC120/VS2013 produces ICE:
#if scope_HAVE( TRAILING_RETURN_TYPE ) && !scope_BETWEEN( scope_COMPILER_MSVC_VERSION, 120, 130 )
template< class RR=R >
auto operator*() const scope_noexcept ->
scope_ENABLE_IF_R_(
std::is_pointer<RR>::value && !std::is_void<typename std::remove_pointer<RR>::type>::value
, typename std::add_lvalue_reference<typename std::remove_pointer<R>::type>::type
)
#else
typename std::add_lvalue_reference<typename std::remove_pointer<R>::type>::type
operator*() const scope_noexcept
#endif
{
return *get();
}
// VC120/VS2013 produces ICE:
#if scope_HAVE( TRAILING_RETURN_TYPE ) && !scope_BETWEEN( scope_COMPILER_MSVC_VERSION, 120, 130 )
template< class RR=R >
auto operator->() const scope_noexcept -> scope_ENABLE_IF_R_( std::is_pointer<RR>::value, R )
#else
R operator->() const scope_noexcept
#endif
{
return get();
}
D const & get_deleter() const scope_noexcept
{
return deleter;
}
scope_is_delete_access:
unique_resource & operator=( unique_resource const & ) scope_is_delete;
unique_resource( unique_resource const & ) scope_is_delete;
private:
R1 resource;
D deleter;
bool execute_on_reset;
};
#if scope_HAVE( DEDUCTION_GUIDES )
template< typename R, typename D >
unique_resource(R, D) -> unique_resource<R, D>;
#endif
// special factory function make_unique_resource_checked():
#if scope_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
template< class R, class D, class S = typename std11::decay<R>::type >
unique_resource
<
typename std11::decay<R>::type
, typename std11::decay<D>::type
>
//make_unique_resource_checked( R && resource, typename std20::type_identity<S>::type const & invalid, D && deleter )
make_unique_resource_checked( R && resource, S const & invalid, D && deleter )
scope_noexcept_op
((
std11::is_nothrow_constructible<typename std11::decay<R>::type, R>::value
&& std11::is_nothrow_constructible<typename std11::decay<D>::type, D>::value
))
{
return unique_resource<typename std11::decay<R>::type, typename std11::decay<D>::type>(
std::forward<R>( resource ), std::forward<D>( deleter ), !bool( resource == invalid ) );
}
#else // scope_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
// avoid default template arguments:
template< class R, class D >
unique_resource
<
typename std11::decay<R>::type
, typename std11::decay<D>::type
>
make_unique_resource_checked( R && resource, R const & invalid, D && deleter )
scope_noexcept_op
((
std11::is_nothrow_constructible<typename std11::decay<R>::type, R>::value
&& std11::is_nothrow_constructible<typename std11::decay<D>::type, D>::value
))
{
return unique_resource<typename std11::decay<R>::type, typename std11::decay<D>::type>(
std::forward<R>( resource ), std::forward<D>( deleter ), !bool( resource == invalid ) );
}
template< class R, class D, class S >
unique_resource
<
typename std11::decay<R>::type
, typename std11::decay<D>::type
>
make_unique_resource_checked( R && resource, S const & invalid, D && deleter )
scope_noexcept_op
((
std11::is_nothrow_constructible<typename std11::decay<R>::type, R>::value
&& std11::is_nothrow_constructible<typename std11::decay<D>::type, D>::value
))
{
return unique_resource<typename std11::decay<R>::type, typename std11::decay<D>::type>(
std::forward<R>( resource ), std::forward<D>( deleter ), !bool( resource == invalid ) );
}
#endif // scope_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
#else // #if scope_USE_POST_CPP98_VERSION
//
// C++98 version:
//
struct on_exit_policy
{
mutable bool invoke_;
on_exit_policy()
: invoke_( true )
{}
on_exit_policy( on_exit_policy const & other )
: invoke_( other.invoke_ )
{
other.invoke_ = false;
}
void release()
{
invoke_ = false;
}
bool perform()
{
return invoke_;
}
};
struct on_fail_policy
{
mutable int ucount_;
on_fail_policy()
: ucount_( std17::uncaught_exceptions() )
{}
on_fail_policy( on_fail_policy const & other )
: ucount_( other.ucount_ )
{
other.ucount_ = std::numeric_limits<int>::max();
}
void release()
{
ucount_ = std::numeric_limits<int>::max();
}
bool perform()
{
return ucount_ < std17::uncaught_exceptions();
}
};
struct on_success_policy
{
mutable int ucount_;
on_success_policy()
: ucount_( std17::uncaught_exceptions() )
{}
on_success_policy( on_success_policy const & other )
: ucount_( other.ucount_ )
{
other.ucount_ = -1;
}
void release()
{
ucount_ = -1;
}
bool perform()
{
return ucount_ >= std17::uncaught_exceptions();
}
};
template< typename Policy >
class scope_guard : public Policy
{
public:
typedef void (*Action)();
template< typename Fn >
scope_guard( Fn action )
: Policy()
, action_( action )
{}
scope_guard( scope_guard const & other )
: Policy( other )
, action_( other.action_ )
{}
virtual ~scope_guard()
{
if ( this->perform() )
action_();
}
private:
scope_guard & operator=( scope_guard const & );
private:
Action action_;
};
class scope_exit : public scope_guard< on_exit_policy >
{
public:
template< typename Fn >
scope_exit( Fn action ) : scope_guard( action ) {}
};
class scope_fail : public scope_guard< on_fail_policy >
{
public:
template< typename Fn >
scope_fail( Fn action ) : scope_guard( action ) {}
};
class scope_success : public scope_guard< on_success_policy >
{
public:
template< typename Fn >
scope_success( Fn action ) : scope_guard( action ) {}
};
// unique_resource (C++98):
template< class R, class D >
class unique_resource
{
public:
unique_resource()
: resource()
, deleter()
, execute_on_reset( false )
{}
template< class RR, class DD >
unique_resource( RR const & r, DD const & d, bool execute = true )
: resource( r )
, deleter( d )
, execute_on_reset( execute )
{}
// 'move' construction
unique_resource( unique_resource const & other )
: resource( other.resource )
, deleter( other.deleter )
, execute_on_reset( other.execute_on_reset )
{
other.execute_on_reset = false; // other.release();
}
~unique_resource()
{
reset();
}
// 'move' assignment
unique_resource & operator=( unique_resource const & other )
{
reset();
resource = other.resource;
deleter = other.deleter;
execute_on_reset = other.execute_on_reset;
other.execute_on_reset = false; // other.release();
return *this;
}
void reset()
{
if ( execute_on_reset )
{
execute_on_reset = false;
get_deleter()( get() );
}
}
template< class RR >
void reset( RR const & r )
try
{
reset();
resource = r;
execute_on_reset = true;
}
catch(...)
{
this->get_deleter()( r );
}
void release()
{
execute_on_reset = false;
}
R const & get() const
{
return resource;
}
typename std11::remove_pointer<R>::type &
operator*() const
{
return *get();
}
R operator->() const
{
return get();
}
D const & get_deleter() const
{
return deleter;
}
private:
// using R1 = conditional_t< is_reference_v<R>, reference_wrapper<remove_reference_t<R>>, R >; // exposition only
// typedef R R1;
R resource;
D deleter;
mutable bool execute_on_reset;
};
template< class EF >
scope_exit make_scope_exit( EF action )
{
return scope_exit( action );
}
template< class EF >
scope_fail make_scope_fail( EF action )
{
return scope_fail( action );
}
template< class EF >
scope_success make_scope_success( EF action )
{
return scope_success( action );
}
template< class R, class D, class S >
unique_resource
<
typename std11::decay<R>::type
, typename std11::decay<D>::type
>
make_unique_resource_checked( R const & resource, S const & invalid, D const & deleter )
{
return unique_resource<typename std11::decay<R>::type, typename std11::decay<D>::type>(
resource,deleter, !bool( resource == invalid ) );
}
#endif // #if scope_USE_POST_CPP98_VERSION
}} // namespace nonstd::scope
//
// Make type available in namespace nonstd:
//
namespace nonstd
{
using scope::scope_exit;
using scope::scope_fail;
using scope::scope_success;
using scope::unique_resource;
using scope::make_scope_exit;
using scope::make_scope_fail;
using scope::make_scope_success;
using scope::make_unique_resource_checked;
}
#endif // scope_USES_STD_SCOPE
#endif // NONSTD_SCOPE_LITE_HPP