// <experimental/scope> -*- C++ -*-

// Copyright The GNU Toolchain Authors.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file experimental/scope
 *  This is a TS C++ Library header.
 *  @ingroup libfund-ts
 */

#ifndef _GLIBCXX_EXPERIMENTAL_SCOPE
#define _GLIBCXX_EXPERIMENTAL_SCOPE 1

#pragma GCC system_header

#include <bits/requires_hosted.h> // experimental is currently omitted

#if __cplusplus >= 202002L

#include <concepts>
#include <exception> // uncaught_exceptions
#include <bits/refwrap.h>

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace experimental::inline fundamentals_v3
{
#define __cpp_lib_experimental_scope 201902

  template<typename _Tp, typename _Up>
    concept __not_same_as = !same_as<_Tp, _Up>;

  template<typename _Tp>
    concept __not_lvalue_ref = !is_lvalue_reference_v<_Tp>;

  template<typename _Ef>
    class [[nodiscard]] scope_exit
    {
    public:
      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_exit>
	      && constructible_from<_Ef, _Efp>
	[[nodiscard]] explicit
	scope_exit(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>)
#ifdef __cpp_exceptions
	try
#endif
	: _M_exit_function(__f)
	{ }
#ifdef __cpp_exceptions
	catch (...) { __f(); }
#endif

      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_exit>
	      && constructible_from<_Ef, _Efp>
	      && __not_lvalue_ref<_Efp>
	      && is_nothrow_constructible_v<_Ef, _Efp>
	explicit
	scope_exit(_Efp&& __f) noexcept
	: _M_exit_function(std::forward<_Efp>(__f))
	{ }

      scope_exit(scope_exit&& __rhs) noexcept
      requires is_nothrow_move_constructible_v<_Ef>
      : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function))
      { __rhs.release(); }

      scope_exit(scope_exit&& __rhs)
      noexcept(is_nothrow_copy_constructible_v<_Ef>)
      requires (!is_nothrow_move_constructible_v<_Ef>)
	    && is_copy_constructible_v<_Ef>
      : _M_exit_function(__rhs._M_exit_function)
      { __rhs.release(); }

      scope_exit(const scope_exit&) = delete;
      scope_exit& operator=(const scope_exit&) = delete;
      scope_exit& operator=(scope_exit&&) = delete;

      ~scope_exit() noexcept
      {
	if (_M_execute_on_destruction)
	  _M_exit_function();
      }

      void release() noexcept { _M_execute_on_destruction = false; }

    private:
      [[no_unique_address]] _Ef _M_exit_function;
      bool _M_execute_on_destruction = true;
    };

  template<typename _Ef>
    scope_exit(_Ef) -> scope_exit<_Ef>;

  template<typename _Ef>
    class [[nodiscard]] scope_fail
    {
    public:
      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_fail>
	      && constructible_from<_Ef, _Efp>
	explicit
	scope_fail(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>)
#ifdef __cpp_exceptions
	try
#endif
	: _M_exit_function(__f)
	{ }
#ifdef __cpp_exceptions
	catch (...) { __f(); }
#endif

      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_fail>
	      && constructible_from<_Ef, _Efp>
	      && __not_lvalue_ref<_Efp>
	      && is_nothrow_constructible_v<_Ef, _Efp>
	explicit
	scope_fail(_Efp&& __f) noexcept
	: _M_exit_function(std::forward<_Efp>(__f))
	{ }

      scope_fail(scope_fail&& __rhs) noexcept
      requires is_nothrow_move_constructible_v<_Ef>
      : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function))
      { __rhs.release(); }

      scope_fail(scope_fail&& __rhs)
      noexcept(is_nothrow_copy_constructible_v<_Ef>)
      requires (!is_nothrow_move_constructible_v<_Ef>)
	    && is_copy_constructible_v<_Ef>
      : _M_exit_function(__rhs._M_exit_function)
      { __rhs.release(); }

      scope_fail(const scope_fail&) = delete;
      scope_fail& operator=(const scope_fail&) = delete;
      scope_fail& operator=(scope_fail&&) = delete;

      ~scope_fail() noexcept
      {
	if (std::uncaught_exceptions() > _M_uncaught_init)
	  _M_exit_function();
      }

      void release() noexcept { _M_uncaught_init = __INT_MAX__; }

    private:
      [[no_unique_address]] _Ef _M_exit_function;
      int _M_uncaught_init = std::uncaught_exceptions();
    };

  template<typename _Ef>
    scope_fail(_Ef) -> scope_fail<_Ef>;

  template<typename _Ef>
    class [[nodiscard]] scope_success
    {
    public:
      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_success>
	      && constructible_from<_Ef, _Efp>
	explicit
	scope_success(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>)
	: _M_exit_function(__f)
	{ }

      template<typename _Efp>
	requires __not_same_as<remove_cvref_t<_Efp>, scope_success>
	      && constructible_from<_Ef, _Efp>
	      && __not_lvalue_ref<_Efp>
	      && is_nothrow_constructible_v<_Ef, _Efp>
	explicit
	scope_success(_Efp&& __f) noexcept
	: _M_exit_function(std::forward<_Efp>(__f))
	{ }

      scope_success(scope_success&& __rhs) noexcept
      requires is_nothrow_move_constructible_v<_Ef>
      : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function))
      { __rhs.release(); }

      scope_success(scope_success&& __rhs)
      noexcept(is_nothrow_copy_constructible_v<_Ef>)
      requires (!is_nothrow_move_constructible_v<_Ef>)
	    && is_copy_constructible_v<_Ef>
      : _M_exit_function(__rhs._M_exit_function)
      { __rhs.release(); }

      scope_success(const scope_success&) = delete;
      scope_success& operator=(const scope_success&) = delete;
      scope_success& operator=(scope_success&&) = delete;

      ~scope_success() noexcept(noexcept(this->_M_exit_function()))
      {
	if (std::uncaught_exceptions() <= _M_uncaught_init)
	  _M_exit_function();
      }

      void release() noexcept { _M_uncaught_init = -__INT_MAX__; }

    private:
      [[no_unique_address]] _Ef _M_exit_function;
      int _M_uncaught_init = std::uncaught_exceptions();
    };

  template<typename _Ef>
    scope_success(_Ef) -> scope_success<_Ef>;

  template<typename _Resrc, typename _Del>
    class [[nodiscard]] unique_resource
    {
      static_assert(!is_rvalue_reference_v<_Resrc>);
      static_assert(!is_reference_v<_Del>);

      struct _Dummy { constexpr void release() { } };

      template<typename _Tp>
	struct _Wrap
	{
	  template<typename _Up>
	    requires is_constructible_v<_Tp, _Up>
	    _Wrap(_Up&&)
	    noexcept(is_nothrow_constructible_v<_Tp, _Up>);

	  template<typename _Up, typename _Del2>
	    requires is_constructible_v<_Tp, _Up>
	    _Wrap(_Up&& __r, _Del2&& __d)
	    noexcept(is_nothrow_constructible_v<_Tp, _Up>)
	    : _M_t(std::forward<_Up>(__r))
	    { __d.release(); }

	  _Wrap() = default;

	  _Wrap(_Wrap&&) = default;

	  _Wrap(_Wrap&& __rhs) noexcept(is_nothrow_constructible_v<_Tp, _Tp&>)
	  requires (!is_nothrow_move_constructible_v<_Tp>)
	  : _M_t(__rhs._M_t)
	  { }

	  _Wrap& operator=(const _Wrap&) = default;

	  _Wrap& operator=(_Wrap&&) = default;

	  constexpr _Tp& get() noexcept { return _M_t; }
	  constexpr const _Tp& get() const noexcept { return _M_t; }

	  [[no_unique_address]] _Tp _M_t{};
	};

      template<typename _Tp>
	struct _Wrap<_Tp&>
	{
	  template<typename _Up>
	    requires is_constructible_v<reference_wrapper<_Tp>, _Up>
	    _Wrap(_Up&&)
	    noexcept(is_nothrow_constructible_v<reference_wrapper<_Tp>, _Up>);

	  template<typename _Up, typename _Del2>
	    _Wrap(_Up&& __r, _Del2&& __d)
	    noexcept(is_nothrow_constructible_v<reference_wrapper<_Tp>, _Up>)
	    : _M_p(__builtin_addressof(static_cast<_Tp&>(__r)))
	    { __d.release(); }

	  _Wrap() = delete;

	  _Wrap(const _Wrap&) = default;

	  _Wrap& operator=(const _Wrap&) = default;

	  _Tp& get() noexcept { return *_M_p; }
	  const _Tp& get() const noexcept { return *_M_p; }

	  _Tp* _M_p = nullptr;
	};

      using                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           