// This file is part of the mcl project. // Copyright (c) 2022 merryhime // SPDX-License-Identifier: MIT #pragma once #include #include #include #include namespace mcl::detail { struct scope_exit_tag {}; struct scope_fail_tag {}; struct scope_success_tag {}; template class scope_exit final { public: explicit scope_exit(Function&& fn) : function(std::move(fn)) {} ~scope_exit() noexcept { function(); } private: Function function; }; template class scope_fail final { public: explicit scope_fail(Function&& fn) : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {} ~scope_fail() noexcept { if (std::uncaught_exceptions() > exception_count) { function(); } } private: Function function; int exception_count; }; template class scope_success final { public: explicit scope_success(Function&& fn) : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {} ~scope_success() { if (std::uncaught_exceptions() <= exception_count) { function(); } } private: Function function; int exception_count; }; // We use ->* here as it has the highest precedence of the operators we can use. template auto operator->*(scope_exit_tag, Function&& function) { return scope_exit>{std::forward(function)}; } template auto operator->*(scope_fail_tag, Function&& function) { return scope_fail>{std::forward(function)}; } template auto operator->*(scope_success_tag, Function&& function) { return scope_success>{std::forward(function)}; } } // namespace mcl::detail #define SCOPE_EXIT auto ANONYMOUS_VARIABLE(MCL_SCOPE_EXIT_VAR_) = ::mcl::detail::scope_exit_tag{}->*[&]() noexcept #define SCOPE_FAIL auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_fail_tag{}->*[&]() noexcept #define SCOPE_SUCCESS auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_success_tag{}->*[&]()