Skip to content
Snippets Groups Projects
Commit 105c69f8 authored by Santiago Ospina De Los Ríos's avatar Santiago Ospina De Los Ríos
Browse files

Simplify code

parent d0902925
No related branches found
No related tags found
No related merge requests found
Pipeline #74348 waiting for manual action
......@@ -13,170 +13,142 @@
#include <functional>
#include <exception>
namespace Dune::Std
{
namespace Dune::Std {
namespace Impl {
enum ScopeHandlerType {Fail, Success, Exit};
namespace Impl
{
enum class ScopeHandlerType
{
Fail,
Success,
Exit
};
//!
/**
* \brief A scope guard to execute functor on scope at exit, fail or success
* \ingroup CxxUtilities
* \details The class template scope_success is a general-purpose scope guard
* intended to call its exit function when a scope is normally exited.
*
* Part of the Version 3 of the C++ Extensions for Library Fundamentals
*/
template <class EF, ScopeHandlerType type>
class ScopeHandler
{
static_assert((type == ScopeHandlerType::Fail) || (type == ScopeHandlerType::Success) || (type == ScopeHandlerType::Exit));
template <class Fn>
static constexpr bool Constructible = not std::is_base_of_v<ScopeHandler, std::remove_cv_t<std::remove_reference_t<Fn>>> and std::is_constructible_v<EF, Fn>;
template <class Fn>
static constexpr bool ForwardConstructor = not std::is_lvalue_reference_v<Fn> and std::is_nothrow_constructible_v<EF, Fn>;
template <class Fn>
static constexpr bool Movable = std::is_base_of_v<ScopeHandler, std::remove_cv_t<std::remove_reference_t<Fn>>> and (std::is_nothrow_move_constructible_v<EF> or std::is_copy_constructible_v<EF>);
EF _fn;
DUNE_NO_UNIQUE_ADDRESS std::conditional_t<type == ScopeHandlerType::Exit, bool, int> _count;
public:
template <class Fn, std::enable_if_t<Constructible<Fn> and ForwardConstructor<Fn>, int> = 0>
[[nodiscard]] explicit ScopeHandler(Fn &&fn) noexcept(std::is_nothrow_constructible_v<EF, Fn>)
: _fn{std::forward<Fn>(fn)}
{
_count = (type == ScopeHandlerType::Exit) ? 1 : std::uncaught_exceptions();
}
//!
/**
* \brief A scope guard to execute functor on scope at exit, fail or success
* \ingroup CxxUtilities
* \details The class template scope_success is a general-purpose scope guard
* intended to call its exit function when a scope is normally exited.
*
* Part of the Version 3 of the C++ Extensions for Library Fundamentals
*/
template<class EF, ScopeHandlerType type>
class ScopeHandler
{
static_assert((type == ScopeHandlerType::Fail) || (type == ScopeHandlerType::Success) || (type == ScopeHandlerType::Exit));
template<class T>
struct IsRefFunction : std::false_type {};
template<class Return>
struct IsRefFunction<Return(&)()> : std::true_type {};
static constexpr bool is_lvalue_function = IsRefFunction<EF>::value;
static constexpr bool is_destructible_function_object = std::is_destructible_v<EF> and std::is_invocable_v<EF>;
static constexpr bool is_lvalue_function_object = std::is_invocable_v<std::remove_reference_t<EF>>;
static_assert(is_lvalue_function or is_destructible_function_object or is_lvalue_function_object);
template<class Fn>
static constexpr bool Constructible = not std::is_base_of_v<ScopeHandler, std::remove_cv_t<std::remove_reference_t<Fn>>> and std::is_constructible_v<EF, Fn>;
template<class Fn>
static constexpr bool ForwardConstructor = not std::is_lvalue_reference_v<Fn> and std::is_nothrow_constructible_v<EF, Fn>;
template<class Fn>
static constexpr bool Movable = std::is_base_of_v<ScopeHandler, std::remove_cv_t<std::remove_reference_t<Fn>>> and (std::is_nothrow_move_constructible_v<EF> or std::is_copy_constructible_v<EF>);
struct Empty {};
EF _fn;
bool _active;
DUNE_NO_UNIQUE_ADDRESS std::conditional_t<type != ScopeHandlerType::Exit,int,Empty> _except_counter;
template<class Fn, class C>
static bool scoped_invoke(Fn& fn, C old_counter = {}) {
if constexpr (type == ScopeHandlerType::Exit) {
std::invoke(fn);
return true;
} else {
auto new_counter = std::uncaught_exceptions();
if ((type == ScopeHandlerType::Fail) and new_counter > old_counter) {
std::invoke(fn);
return true;
} else if ((type == ScopeHandlerType::Success) and new_counter <= old_counter) {
std::invoke(fn);
return true;
// construct from a function that may throw on construction (invoke function in case of failure)
template <class Fn, std::enable_if_t<Constructible<Fn> and not ForwardConstructor<Fn>, int> = 0>
[[nodiscard]] explicit ScopeHandler(Fn &&fn) noexcept(std::is_nothrow_constructible_v<EF, Fn &>)
try : _fn{fn}
{
_count = (type == ScopeHandlerType::Exit) ? 1 : std::uncaught_exceptions();
}
catch (...)
{
// success type does not require to call the exit function if constructor fails
if constexpr (type != ScopeHandlerType::Success)
std::invoke(fn);
}
}
return false;
}
public:
template <class SH = ScopeHandler, std::enable_if_t<Movable<SH> and std::is_nothrow_move_constructible_v<EF>, int> = 0>
[[nodiscard]] ScopeHandler(SH &&other) noexcept(std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: _fn{std::forward<EF>(other._fn)}, _count{other._count}
{
other.release();
}
template <class SH = ScopeHandler, std::enable_if_t<Movable<SH> and not std::is_nothrow_move_constructible_v<EF>, int> = 0>
[[nodiscard]] ScopeHandler(SH &&other) noexcept(std::is_nothrow_copy_constructible_v<EF>)
: _fn{other._fn}, _count{other._count}
{
other.release();
}
template<class Fn, std::enable_if_t<Constructible<Fn> and ForwardConstructor<Fn>, int> = 0>
[[nodiscard]] explicit ScopeHandler(Fn&& fn)
: _fn{std::forward<Fn>(fn)}
, _active{true}
{
if constexpr (type != ScopeHandlerType::Exit)
_except_counter = std::uncaught_exceptions();
void release() noexcept
{
if (type == ScopeHandlerType::Exit)
_count = false;
else if (type == ScopeHandlerType::Fail)
_count = std::numeric_limits<int>::max();
else if (type == ScopeHandlerType::Success)
_count = std::numeric_limits<int>::min();
};
ScopeHandler &operator=(ScopeHandler) = delete;
~ScopeHandler() noexcept
{
bool do_invoke = false;
if constexpr (type == ScopeHandlerType::Exit)
{
do_invoke = _count;
}
else
{
auto new_count = std::uncaught_exceptions();
do_invoke = ((type == ScopeHandlerType::Fail) and (new_count > _count)) or ((type == ScopeHandlerType::Success) and (new_count <= _count));
}
if (do_invoke)
{
std::invoke(_fn);
release();
}
}
};
}
// construct from a function that does throw on construction (invoke function in case of failure)
template<class Fn, std::enable_if_t<Constructible<Fn> and not ForwardConstructor<Fn>, int> = 0>
[[nodiscard]] explicit ScopeHandler(Fn&& fn) noexcept(std::is_nothrow_constructible_v<EF, Fn> or std::is_nothrow_constructible_v<EF, Fn&>)
try
: _fn{fn}
, _active{true}
template <class Fn>
struct scope_exit : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Exit>
{
if constexpr (type != ScopeHandlerType::Exit)
_except_counter = std::uncaught_exceptions();
}
catch (...)
{
// success type does not require to call the exit function if constructor fails
if constexpr (type != ScopeHandlerType::Success)
std::invoke(fn);
}
using Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Exit>::ScopeHandler;
};
template<class Fn = ScopeHandler, std::enable_if_t<Movable<Fn> and std::is_nothrow_move_constructible_v<EF>, int> = 0>
[[nodiscard]] ScopeHandler(Fn&& other) noexcept(std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: _fn{ std::forward<EF>(other._fn) }
, _active{ other._active }
{
if constexpr (type != ScopeHandlerType::Exit)
_except_counter = other._except_counter;
other.release();
}
// additional deduction guide
template <class Fn>
scope_exit(Fn fn) -> scope_exit<Fn>;
template<class Fn = ScopeHandler, std::enable_if_t<Movable<Fn> and not std::is_nothrow_move_constructible_v<EF>, int> = 0>
[[nodiscard]] ScopeHandler(Fn&& other) noexcept(std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: _fn{ other._fn }
, _active{ other._active }
template <class Fn>
struct scope_fail : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Fail>
{
if constexpr (type != ScopeHandlerType::Exit)
_except_counter = other._except_counter;
other.release();
}
void release() noexcept {_active = false; };
ScopeHandler& operator=(ScopeHandler) = delete;
~ScopeHandler() noexcept {
if (_active) {
bool do_release = ScopeHandler::scoped_invoke(_fn, _except_counter);
if (do_release)
release();
}
}
};
using Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Fail>::ScopeHandler;
};
}
// additional deduction guide
template <class Fn>
scope_fail(Fn fn) -> scope_fail<Fn>;
template<class Fn>
class scope_exit : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Exit> {
using Base = Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Exit>;
public:
using Base::Base;
};
// additional deduction guide
template<class Fn>
scope_exit(Fn fn) -> scope_exit<Fn>;
template<class Fn>
class scope_fail : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Fail> {
using Base = Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Fail>;
public:
using Base::Base;
};
// additional deduction guide
template<class Fn>
scope_fail(Fn fn) -> scope_fail<Fn>;
template<class Fn>
class scope_success : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Success> {
using Base = Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Success>;
public:
using Base::Base;
};
template <class Fn>
struct scope_success : public Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Success>
{
using Impl::ScopeHandler<Fn, Impl::ScopeHandlerType::Success>::ScopeHandler;
};
// additional deduction guide
template<class Fn>
scope_success(Fn fn) -> scope_success<Fn>;
// additional deduction guide
template <class Fn>
scope_success(Fn fn) -> scope_success<Fn>;
} // namespace Dune::Std
......
......@@ -21,7 +21,7 @@ dune_add_test(SOURCES mdarraytest.cc
LABELS quick
LINK_LIBRARIES Dune::Common)
dune_add_test(SOURCES scope.cc
dune_add_test(SOURCES scopetest.cc
LINK_LIBRARIES dunecommon
LABELS quick)
......
......@@ -98,21 +98,6 @@ int main(int argc, char **argv)
testScope(test, f0_factory);
auto f1_factory = []{return f1;};
testScope(test, f1_factory);
{
struct F {
F() {}
F(F&&) = delete;
F(const F&) { throw;}
void operator()() {
++status;
}
};
// TODO: check that this fails to compile
// Dune::Std::scope_exit se{F{}};
// because:
static_assert(not std::is_constructible_v<F, F>);
}
{
struct F {
F() {}
......@@ -157,7 +142,36 @@ int main(int argc, char **argv)
// since move constructor may throw, it chooses copy constructor
testScopeMoveThrow(test, []{return I{};});
}
// check reverse order of deletion
{
int exit_count = 0;
Dune::Std::scope_exit scp_exit{[&]{
test.check(3 == exit_count++, "Wrong exit count - Exit");
}};
Dune::Std::scope_success scp_success{[&]{
test.check(2 == exit_count++, "Wrong exit count - Success");
}};
try {
Dune::Std::scope_fail scp_fail{[&]{
test.check(0 == exit_count++, "Wrong exit count - Fail 0");
}};
throw std::runtime_error{"0"};
} catch (...) {
try {
Dune::Std::scope_fail scp_fail{[&]{
test.check(1 == exit_count++, "Wrong exit count - Fail 1");
}};
throw std::runtime_error{"1"};
} catch (...) {
Dune::Std::scope_fail scp_fail{[&]{
test.check(false, "Wrong exit count - Fail NEVER");
}};
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment