> | > | ||||
explicit scope_guard_base(EFP&& exitFunction) noexcept(std::is_nothrow_constructible_v<EF, EFP> | explicit scope_guard_base(EFP&& exitFunction) noexcept(std::is_nothrow_constructible_v<EF, EFP> | ||||
|| std::is_nothrow_constructible_v<EF, EFP&>) | || std::is_nothrow_constructible_v<EF, EFP&>) | ||||
: m_exitfunction(std::forward<EFP>(exitFunction)), | |||||
m_execute_on_destruction(true) | |||||
: exitfunction(std::forward<EFP>(exitFunction)), | |||||
execute_on_destruction(true) | |||||
{ | { | ||||
} | } | ||||
std::enable_if_t<std::is_constructible_v<EF, EFP>, int> = 0, | std::enable_if_t<std::is_constructible_v<EF, EFP>, int> = 0, | ||||
std::enable_if_t<std::is_lvalue_reference_v<EFP>, int> = 0 | std::enable_if_t<std::is_lvalue_reference_v<EFP>, int> = 0 | ||||
> | > | ||||
explicit scope_guard_base(EFP&& exitFunction) try : m_exitfunction(exitFunction), | |||||
m_execute_on_destruction(true) | |||||
explicit scope_guard_base(EFP&& exitFunction) try : exitfunction(exitFunction), | |||||
execute_on_destruction(true) | |||||
{ | { | ||||
} | } | ||||
catch( ... ) | catch( ... ) | ||||
scope_guard_base(scope_guard_base&& other) noexcept(std::is_nothrow_move_constructible_v<EF> | scope_guard_base(scope_guard_base&& other) noexcept(std::is_nothrow_move_constructible_v<EF> | ||||
|| std::is_nothrow_copy_constructible_v<EF>) | || std::is_nothrow_copy_constructible_v<EF>) | ||||
: Strategy(other), | : Strategy(other), | ||||
m_exitfunction(forward_if_nothrow_move_constructible(other.m_exitfunction)), | |||||
m_execute_on_destruction(other.m_execute_on_destruction) | |||||
exitfunction(forward_if_nothrow_move_constructible(other.exitfunction)), | |||||
execute_on_destruction(other.execute_on_destruction) | |||||
{ | { | ||||
other.release(); | other.release(); | ||||
} | } | ||||
~scope_guard_base() noexcept(is_noexcept_dtor_v<EF, Strategy>) | ~scope_guard_base() noexcept(is_noexcept_dtor_v<EF, Strategy>) | ||||
{ | { | ||||
if( (m_execute_on_destruction == true) && (this->should_execute() == true) ) | |||||
if( (execute_on_destruction == true) && (this->should_execute() == true) ) | |||||
{ | { | ||||
m_exitfunction(); | |||||
exitfunction(); | |||||
} | } | ||||
} | } | ||||
void release() noexcept | void release() noexcept | ||||
{ | { | ||||
m_execute_on_destruction = false; | |||||
execute_on_destruction = false; | |||||
} | } | ||||
private: | private: | ||||
EF m_exitfunction; | |||||
bool m_execute_on_destruction; | |||||
EF exitfunction; | |||||
bool execute_on_destruction; | |||||
}; | }; | ||||
} | } |
public: | public: | ||||
template<class TT, class G, std::enable_if_t<std::is_constructible_v<T, TT>, int> = 0> | template<class TT, class G, std::enable_if_t<std::is_constructible_v<T, TT>, int> = 0> | ||||
Wrapper(TT&& value, G&& g) noexcept(std::is_nothrow_constructible_v<T, TT>) : m_value(std::forward<TT>(value)) | |||||
Wrapper(TT&& v, G&& g) noexcept(std::is_nothrow_constructible_v<T, TT>) : value(std::forward<TT>(v)) | |||||
{ | { | ||||
g.release(); | g.release(); | ||||
} | } | ||||
T& get() noexcept | T& get() noexcept | ||||
{ | { | ||||
return m_value; | |||||
return value; | |||||
} | } | ||||
const T& get() const noexcept | const T& get() const noexcept | ||||
{ | { | ||||
return m_value; | |||||
return value; | |||||
} | } | ||||
void reset(Wrapper<T>&& other) noexcept | void reset(Wrapper<T>&& other) noexcept | ||||
{ | { | ||||
m_value = std::move(other.m_value); | |||||
value = std::move(other.value); | |||||
} | } | ||||
void reset(T&& newValue) noexcept(std::is_nothrow_assignable_v<T, decltype(std::move_if_noexcept(newValue))>) | void reset(T&& newValue) noexcept(std::is_nothrow_assignable_v<T, decltype(std::move_if_noexcept(newValue))>) | ||||
{ | { | ||||
m_value = std::forward<T>(newValue); | |||||
value = std::forward<T>(newValue); | |||||
} | } | ||||
void reset(const T& newValue) noexcept(std::is_nothrow_assignable_v<T, const T&>) | void reset(const T& newValue) noexcept(std::is_nothrow_assignable_v<T, const T&>) | ||||
{ | { | ||||
m_value = newValue; | |||||
value = newValue; | |||||
} | } | ||||
private: | private: | ||||
T m_value; | |||||
T value; | |||||
}; | }; | ||||
public: | public: | ||||
template<class TT, class G, std::enable_if_t<std::is_convertible_v<TT, T&>, int> = 0> | template<class TT, class G, std::enable_if_t<std::is_convertible_v<TT, T&>, int> = 0> | ||||
Wrapper(TT&& value, G&& g) noexcept(std::is_nothrow_constructible_v<TT, T&>) : m_value(static_cast<T&>(value)) | |||||
Wrapper(TT&& v, G&& g) noexcept(std::is_nothrow_constructible_v<TT, T&>) : value(static_cast<T&>(v)) | |||||
{ | { | ||||
g.release(); | g.release(); | ||||
} | } | ||||
T& get() noexcept | T& get() noexcept | ||||
{ | { | ||||
return m_value.get(); | |||||
return value.get(); | |||||
} | } | ||||
const T& get() const noexcept | const T& get() const noexcept | ||||
{ | { | ||||
return m_value.get(); | |||||
return value.get(); | |||||
} | } | ||||
void reset(Wrapper<T>&& other) noexcept | void reset(Wrapper<T>&& other) noexcept | ||||
{ | { | ||||
m_value = std::move(other.m_value); | |||||
value = std::move(other.value); | |||||
} | } | ||||
void reset(T& newValue) noexcept | void reset(T& newValue) noexcept | ||||
{ | { | ||||
m_value = std::ref(newValue); | |||||
value = std::ref(newValue); | |||||
} | } | ||||
private: | private: | ||||
type m_value; | |||||
type value; | |||||
}; | }; | ||||
} | } |
{ | { | ||||
bool should_execute() const noexcept | bool should_execute() const noexcept | ||||
{ | { | ||||
return std::uncaught_exceptions() > m_uncaught_on_creation; | |||||
return std::uncaught_exceptions() > uncaught_on_creation; | |||||
} | } | ||||
int m_uncaught_on_creation = std::uncaught_exceptions(); | |||||
int uncaught_on_creation = std::uncaught_exceptions(); | |||||
}; | }; | ||||
} | } |
{ | { | ||||
bool should_execute() const noexcept | bool should_execute() const noexcept | ||||
{ | { | ||||
return std::uncaught_exceptions() <= m_uncaught_on_creation; | |||||
return std::uncaught_exceptions() <= uncaught_on_creation; | |||||
} | } | ||||
int m_uncaught_on_creation = std::uncaught_exceptions(); | |||||
int uncaught_on_creation = std::uncaught_exceptions(); | |||||
}; | }; | ||||
> | > | ||||
explicit unique_resource(RR&& r, DD&& d) noexcept((std::is_nothrow_constructible_v<R, RR> || std::is_nothrow_constructible_v<R, RR&>) | explicit unique_resource(RR&& r, DD&& d) noexcept((std::is_nothrow_constructible_v<R, RR> || std::is_nothrow_constructible_v<R, RR&>) | ||||
&& (std::is_nothrow_constructible_v<D, DD> || std::is_nothrow_constructible_v<D, DD&>)) | && (std::is_nothrow_constructible_v<D, DD> || std::is_nothrow_constructible_v<D, DD&>)) | ||||
: m_resource(detail::forward_if_nothrow_constructible<R, RR>(std::forward<RR>(r)), scope_exit{[&r, &d] { d(r); }}), | |||||
m_deleter(detail::forward_if_nothrow_constructible<D, DD>(std::forward<DD>(d)), scope_exit{[this, &d] { d(get()); }}), | |||||
m_execute_on_destruction(true) | |||||
: resource(detail::forward_if_nothrow_constructible<R, RR>(std::forward<RR>(r)), scope_exit{[&r, &d] { d(r); }}), | |||||
deleter(detail::forward_if_nothrow_constructible<D, DD>(std::forward<DD>(d)), scope_exit{[this, &d] { d(get()); }}), | |||||
execute_on_destruction(true) | |||||
{ | { | ||||
} | } | ||||
unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v<R> | unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible_v<R> | ||||
&& std::is_nothrow_move_constructible_v<D>) | && std::is_nothrow_move_constructible_v<D>) | ||||
: m_resource(std::move_if_noexcept(other.m_resource.get()), scope_exit{[] { }}), | |||||
m_deleter(std::move_if_noexcept(other.m_deleter.get()), scope_exit{[&other] { | |||||
other.get_deleter()(other.m_resource.get()); | |||||
: resource(std::move_if_noexcept(other.resource.get()), scope_exit{[] { }}), | |||||
deleter(std::move_if_noexcept(other.deleter.get()), scope_exit{[&other] { | |||||
other.get_deleter()(other.resource.get()); | |||||
other.release(); }}), | other.release(); }}), | ||||
m_execute_on_destruction(std::exchange(other.m_execute_on_destruction, false)) | |||||
execute_on_destruction(std::exchange(other.execute_on_destruction, false)) | |||||
{ | { | ||||
} | } | ||||
void reset() noexcept | void reset() noexcept | ||||
{ | { | ||||
if( m_execute_on_destruction == true ) | |||||
if( execute_on_destruction == true ) | |||||
{ | { | ||||
m_execute_on_destruction = false; | |||||
get_deleter()(m_resource.get()); | |||||
execute_on_destruction = false; | |||||
get_deleter()(resource.get()); | |||||
} | } | ||||
} | } | ||||
if constexpr( std::is_nothrow_assignable_v<R1&, RR> == true ) | if constexpr( std::is_nothrow_assignable_v<R1&, RR> == true ) | ||||
{ | { | ||||
m_resource.reset(std::forward<RR>(r)); | |||||
resource.reset(std::forward<RR>(r)); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
m_resource.reset(std::as_const(r)); | |||||
resource.reset(std::as_const(r)); | |||||
} | } | ||||
m_execute_on_destruction = true; | |||||
execute_on_destruction = true; | |||||
se.release(); | se.release(); | ||||
} | } | ||||
void release() noexcept | void release() noexcept | ||||
{ | { | ||||
m_execute_on_destruction = false; | |||||
execute_on_destruction = false; | |||||
} | } | ||||
const R& get() const noexcept | const R& get() const noexcept | ||||
{ | { | ||||
return m_resource.get(); | |||||
return resource.get(); | |||||
} | } | ||||
template<class RR = R, std::enable_if_t<std::is_pointer_v<RR>, int> = 0> | template<class RR = R, std::enable_if_t<std::is_pointer_v<RR>, int> = 0> | ||||
RR operator->() const noexcept | RR operator->() const noexcept | ||||
{ | { | ||||
return m_resource.get(); | |||||
return resource.get(); | |||||
} | } | ||||
template<class RR = R, | template<class RR = R, | ||||
const D& get_deleter() const noexcept | const D& get_deleter() const noexcept | ||||
{ | { | ||||
return m_deleter.get(); | |||||
return deleter.get(); | |||||
} | } | ||||
{ | { | ||||
if constexpr( std::is_nothrow_move_assignable_v<DD> == true ) | if constexpr( std::is_nothrow_move_assignable_v<DD> == true ) | ||||
{ | { | ||||
m_resource.reset(std::move(other.m_resource)); | |||||
m_deleter.reset(std::move(other.m_deleter)); | |||||
resource.reset(std::move(other.resource)); | |||||
deleter.reset(std::move(other.deleter)); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
m_deleter.reset(other.m_deleter); | |||||
m_resource.reset(std::move(other.m_resource)); | |||||
deleter.reset(other.deleter); | |||||
resource.reset(std::move(other.resource)); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if constexpr( std::is_nothrow_move_assignable_v<DD> == true ) | if constexpr( std::is_nothrow_move_assignable_v<DD> == true ) | ||||
{ | { | ||||
m_resource.reset(other.m_resource); | |||||
m_deleter.reset(std::move(other.m_deleter)); | |||||
resource.reset(other.resource); | |||||
deleter.reset(std::move(other.deleter)); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
m_resource.reset(other.m_resource); | |||||
m_deleter.reset(other.m_deleter); | |||||
resource.reset(other.resource); | |||||
deleter.reset(other.deleter); | |||||
} | } | ||||
} | } | ||||
m_execute_on_destruction = std::exchange(other.m_execute_on_destruction, false); | |||||
execute_on_destruction = std::exchange(other.execute_on_destruction, false); | |||||
} | } | ||||
return *this; | return *this; | ||||
} | } | ||||
private: | private: | ||||
detail::Wrapper<R> m_resource; | |||||
detail::Wrapper<D> m_deleter; | |||||
bool m_execute_on_destruction; | |||||
detail::Wrapper<R> resource; | |||||
detail::Wrapper<D> deleter; | |||||
bool execute_on_destruction; | |||||
}; | }; | ||||
struct NotNothrowMoveMock | struct NotNothrowMoveMock | ||||
{ | { | ||||
explicit NotNothrowMoveMock(CallMock* m) : m_mock(m) | |||||
explicit NotNothrowMoveMock(CallMock* m) : mock(m) | |||||
{ | { | ||||
} | } | ||||
NotNothrowMoveMock(const NotNothrowMoveMock& other) : m_mock(other.m_mock) | |||||
NotNothrowMoveMock(const NotNothrowMoveMock& other) : mock(other.mock) | |||||
{ | { | ||||
} | } | ||||
NotNothrowMoveMock(NotNothrowMoveMock&& other) noexcept(false) : m_mock(other.m_mock) | |||||
NotNothrowMoveMock(NotNothrowMoveMock&& other) noexcept(false) : mock(other.mock) | |||||
{ | { | ||||
} | } | ||||
void operator()() const | void operator()() const | ||||
{ | { | ||||
m_mock->deleter(); | |||||
mock->deleter(); | |||||
} | } | ||||
NotNothrowMoveMock& operator=(const NotNothrowMoveMock&) | NotNothrowMoveMock& operator=(const NotNothrowMoveMock&) | ||||
} | } | ||||
CallMock* m_mock; | |||||
CallMock* mock; | |||||
}; | }; | ||||
struct ConditialThrowOnCopyMock | struct ConditialThrowOnCopyMock | ||||
{ | { | ||||
explicit ConditialThrowOnCopyMock(Handle h, bool shouldThrow) : m_handle(h), | |||||
m_shouldThrow(shouldThrow) | |||||
explicit ConditialThrowOnCopyMock(Handle h, bool throwOnCopyMock) : handle(h), | |||||
shouldThrow(throwOnCopyMock) | |||||
{ | { | ||||
} | } | ||||
ConditialThrowOnCopyMock(const ConditialThrowOnCopyMock& other) : m_handle(other.m_handle), | |||||
m_shouldThrow(other.m_shouldThrow) | |||||
ConditialThrowOnCopyMock(const ConditialThrowOnCopyMock& other) : handle(other.handle), | |||||
shouldThrow(other.shouldThrow) | |||||
{ | { | ||||
if( m_shouldThrow == true ) | |||||
if( shouldThrow == true ) | |||||
{ | { | ||||
throw std::exception{}; | throw std::exception{}; | ||||
} | } | ||||
{ | { | ||||
if( &other != this ) | if( &other != this ) | ||||
{ | { | ||||
m_handle = other.m_handle; | |||||
m_shouldThrow = other.m_shouldThrow; | |||||
handle = other.handle; | |||||
shouldThrow = other.shouldThrow; | |||||
if( m_shouldThrow == true ) | |||||
if( shouldThrow == true ) | |||||
{ | { | ||||
throw std::exception{}; | throw std::exception{}; | ||||
} | } | ||||
ConditialThrowOnCopyMock& operator=(ConditialThrowOnCopyMock&&) = default; | ConditialThrowOnCopyMock& operator=(ConditialThrowOnCopyMock&&) = default; | ||||
Handle m_handle; | |||||
bool m_shouldThrow; | |||||
Handle handle; | |||||
bool shouldThrow; | |||||
}; | }; | ||||
struct NotNothrowAssignable | struct NotNothrowAssignable | ||||
{ | { | ||||
explicit NotNothrowAssignable(int value) : m_value(value) { } | |||||
explicit NotNothrowAssignable(int v) : value(v) { } | |||||
NotNothrowAssignable(const NotNothrowAssignable&) = default; | NotNothrowAssignable(const NotNothrowAssignable&) = default; | ||||
NotNothrowAssignable& operator=(const NotNothrowAssignable& other) | NotNothrowAssignable& operator=(const NotNothrowAssignable& other) | ||||
{ | { | ||||
if( this != &other ) | if( this != &other ) | ||||
{ | { | ||||
assignNotNoexcept(other.m_value); | |||||
assignNotNoexcept(other.value); | |||||
} | } | ||||
return *this; | return *this; | ||||
} | } | ||||
void assignNotNoexcept(int value) noexcept(false) | |||||
void assignNotNoexcept(int v) noexcept(false) | |||||
{ | { | ||||
m_value = value; | |||||
value = v; | |||||
} | } | ||||
int m_value; | |||||
int value; | |||||
}; | }; | ||||
struct CopyMock | struct CopyMock |
{ | { | ||||
REQUIRE_CALL(m, deleter(3)); | REQUIRE_CALL(m, deleter(3)); | ||||
REQUIRE_CALL(m, deleter(7)); | REQUIRE_CALL(m, deleter(7)); | ||||
auto d = [](const auto& v) { deleter(v.m_value); }; | |||||
auto d = [](const auto& v) { deleter(v.value); }; | |||||
auto guard = sr::unique_resource{NotNothrowAssignable{3}, d}; | auto guard = sr::unique_resource{NotNothrowAssignable{3}, d}; | ||||
const NotNothrowAssignable h{7}; | const NotNothrowAssignable h{7}; | ||||
guard.reset(h); | guard.reset(h); | ||||
{ | { | ||||
REQUIRE_CALL(m, deleter(3)); | REQUIRE_CALL(m, deleter(3)); | ||||
REQUIRE_CALL(m, deleter(7)); | REQUIRE_CALL(m, deleter(7)); | ||||
auto d = [](const auto& v) { deleter(v.m_handle); }; | |||||
auto d = [](const auto& v) { deleter(v.handle); }; | |||||
auto guard = sr::unique_resource{ConditialThrowOnCopyMock{3, false}, d}; | auto guard = sr::unique_resource{ConditialThrowOnCopyMock{3, false}, d}; | ||||
guard.reset(ConditialThrowOnCopyMock{7, true}); | guard.reset(ConditialThrowOnCopyMock{7, true}); | ||||
} | } |