    // Copyright (C) 2023  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SCOPE_H_
    #define DLIB_SCOPE_H_

    #include <utility>
    #include <functional>
    #include <type_traits>

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        template<class Fn>
        class scope_exit
        {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This is a standard's compliant backport of std::experimental::scope_exit that works with C++14.

                Therefore, refer to https://en.cppreference.com/w/cpp/experimental/scope_exit for docs on the
                interface of scope_exit.
        !*/

        private:
            Fn f_;
            bool active_{true};

        public:
            constexpr scope_exit()                                = delete;
            constexpr scope_exit(const scope_exit &)              = delete;
            constexpr scope_exit &operator=(const scope_exit &)   = delete;
            constexpr scope_exit &operator=(scope_exit &&)        = delete;

            constexpr scope_exit(scope_exit &&other) noexcept(std::is_nothrow_move_constructible<Fn>::value)
            : f_{std::move(other.f_)}, active_{std::exchange(other.active_, false)}
            {}

            template<
            class F,
            std::enable_if_t<!std::is_same<std::decay_t<F>, scope_exit>::value, bool> = true
            >
            explicit scope_exit(F&& f) noexcept(std::is_nothrow_constructible<Fn,F>::value)
            : f_{std::forward<F>(f)}, active_{true}
            {}

            ~scope_exit() noexcept
            {
                if (active_)
                    f_();
            }

            void release() noexcept { active_ = false; }
        };

        template<class Fn>
        auto make_scope_exit(Fn&& f)
        /*!
            ensures:
                - This is factory function that wraps the callback in a scope_exit object.
        !*/

        {
            return scope_exit<std::decay_t<Fn>>(std::forward<Fn>(f));
        }

    #ifdef __cpp_deduction_guides
        template<class Fn>
        scope_exit(Fn) -> scope_exit<Fn>;
    #endif

    // ----------------------------------------------------------------------------------------

        using scope_exit_erased = scope_exit<std::function<void()>>;
        /*!
            WHAT THIS OBJECT REPRESENTS
                This is a type erased version of scope_exit. I.e. there is no template parameter.
                Use this object if you wish to hide the exact function signature, for example
                if splitting a declaration and definition across a header file and cpp file.
                This does come at a slight performance penalty since it may incur a heap allocation
                and due to a pointer indirection, the compiler may not inline your callback.
        !*/

    // ----------------------------------------------------------------------------------------

    }

    #endif //DLIB_SCOPE_H_