Browse Source

Merge branch 'unique_resource_move_fixes' into development

Fixes #71, fixes #67, fixes #66, fixes #63, fixes #58, fixes #51,
fixes #65.
main
offa 7 years ago
parent
commit
c02a811f7d
2 changed files with 120 additions and 73 deletions
  1. +7
    -7
      .travis.yml
  2. +113
    -66
      include/unique_resource.h

+ 7
- 7
.travis.yml View File

packages: packages:
- g++-5 - g++-5
sources: *sources sources: *sources
- env: CXX=clang++-5.0 CC=clang-5.0
addons:
apt:
packages:
- clang-5.0
- libc++-dev
sources: *sources
#- env: CXX=clang++-5.0 CC=clang-5.0
#addons:
#apt:
#packages:
#- clang-5.0
#- libc++-dev
#sources: *sources
- env: CXX=clang++-4.0 CC=clang-4.0 - env: CXX=clang++-4.0 CC=clang-4.0
addons: addons:
apt: apt:

+ 113
- 66
include/unique_resource.h View File

#include "scope_exit.h" #include "scope_exit.h"
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include <functional>


namespace sr namespace sr
{ {
constexpr auto is_nothrow_move_or_copy_constructible_from_v = is_ntmocp_constructible<T, TT>::value; constexpr auto is_nothrow_move_or_copy_constructible_from_v = is_ntmocp_constructible<T, TT>::value;




template<class T,
class U = std::conditional_t<(!std::is_nothrow_move_assignable<T>::value
&& std::is_copy_assignable<T>::value),
const T&,
T &&>>
constexpr U move_assign_if_noexcept(T& value) noexcept
template<class T, class U = std::conditional_t<std::is_nothrow_move_constructible<T>::value, T&&, const T&>>
constexpr U forward_if_nothrow_move_constructible(T&& value) noexcept
{ {
return std::move(value);
return std::forward<T>(value);
} }




template<class T, class U = std::conditional_t<std::is_nothrow_move_constructible<T>::value, T&&, const T&>>
constexpr U forward_if_nothrow_move_constructible(T&& value)

template<class T>
struct Wrapper
{ {
return std::forward<T>(value);
}
template<class TT, class G, std::enable_if_t<std::is_constructible<T, TT>::value, int> = 0>
explicit Wrapper(TT&& value, G&& g) noexcept(noexcept(Wrapper{value})) : Wrapper(value)
{
g.release();
}




T& get() noexcept
{
return m_value;
}


template<class R, class D>
class unique_resource
const T& get() const noexcept
{
return m_value;
}

void reset(T&& newValue) noexcept(std::is_nothrow_assignable<T, decltype(std::move_if_noexcept(newValue))>::value)
{
m_value = std::move_if_noexcept(newValue);
}

void reset(const T& newValue) noexcept(std::is_nothrow_assignable<T, const T&>::value)
{
m_value = newValue;
}


private:

Wrapper(const T& value) noexcept(noexcept(T{value})) : m_value(value)
{
}

Wrapper(T&& value) noexcept(noexcept(T{std::move_if_noexcept(value)})) : m_value(std::move_if_noexcept(value))
{
}


T m_value;
};

template<class T>
struct Wrapper<T&>
{ {
public:
template<class TT, class G, std::enable_if_t<std::is_convertible<TT, T&>::value, int> = 0>
explicit Wrapper(TT&& value, G&& g) noexcept(noexcept(static_cast<T&>(value))) : m_value(static_cast<T&>(value))
{
g.release();
}


template<class RR, class DD,
std::enable_if_t<(!std::is_lvalue_reference<RR>::value)
&& std::is_nothrow_constructible<R, RR>::value, int> = 0,
std::enable_if_t<(!std::is_lvalue_reference<DD>::value)
&& std::is_nothrow_constructible<D, DD>::value, int> = 0,
std::enable_if_t<(std::is_copy_constructible<R>::value || std::is_nothrow_move_constructible<R>::value)
&& (std::is_copy_constructible<D>::value || std::is_nothrow_move_constructible<D>::value), int> = 0,
std::enable_if_t<is_nothrow_move_or_copy_constructible_from_v<R, RR>, int> = 0,
std::enable_if_t<is_nothrow_move_or_copy_constructible_from_v<D, DD>, int> = 0
>
explicit unique_resource(RR&& r, DD&& d) noexcept(std::is_nothrow_constructible<R,RR>::value
&& std::is_nothrow_constructible<D, DD>::value)
: m_resource(std::move(r)),
m_deleter(std::move(d)),
m_execute_on_destruction(true)

T& get() noexcept
{
return m_value.get();
}

const T& get() const noexcept
{ {
return m_value.get();
} }


void reset(T& newValue) noexcept
{
m_value = std::ref(newValue);
}


private:

std::reference_wrapper<T> m_value;
};




template<class R, class D>
class unique_resource
{
public:

template<class RR, class DD, template<class RR, class DD,
std::enable_if_t<std::is_lvalue_reference<RR>::value || std::is_lvalue_reference<DD>::value, int> = 0,
std::enable_if_t<(std::is_copy_constructible<R>::value || std::is_nothrow_move_constructible<R>::value) std::enable_if_t<(std::is_copy_constructible<R>::value || std::is_nothrow_move_constructible<R>::value)
&& (std::is_copy_constructible<D>::value || std::is_nothrow_move_constructible<D>::value), int> = 0, && (std::is_copy_constructible<D>::value || std::is_nothrow_move_constructible<D>::value), int> = 0,
std::enable_if_t<is_nothrow_move_or_copy_constructible_from_v<R, RR>, int> = 0, std::enable_if_t<is_nothrow_move_or_copy_constructible_from_v<R, RR>, int> = 0,
> >
explicit unique_resource(RR&& r, DD&& d) noexcept(std::is_nothrow_constructible<R, RR>::value explicit unique_resource(RR&& r, DD&& d) noexcept(std::is_nothrow_constructible<R, RR>::value
&& std::is_nothrow_constructible<D, DD>::value) && std::is_nothrow_constructible<D, DD>::value)
try : m_resource(r),
m_deleter(d),
m_execute_on_destruction(true)
{
}
catch( ... )
{
d(r);
}

template<class TR = R, std::enable_if_t<std::is_nothrow_move_constructible<TR>::value, int> = 0>
unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible<R>::value
&& std::is_nothrow_move_constructible<D>::value)
: m_resource(forward_if_nothrow_move_constructible<R>(std::forward<R>(other.m_resource))),
m_deleter(forward_if_nothrow_move_constructible<D>(std::forward<D>(other.m_deleter))),
m_execute_on_destruction(std::exchange(other.m_execute_on_destruction, false))
: m_resource(std::forward<RR>(r), make_scope_exit([&r, &d] { d(r); })),
m_deleter(std::forward<DD>(d), make_scope_exit([this, &d] { d(get()); })),
m_execute_on_destruction(true)
{ {
} }


template<class TR = R, std::enable_if_t<!std::is_nothrow_move_constructible<TR>::value, int> = 0>
unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible<R>::value unique_resource(unique_resource&& other) noexcept(std::is_nothrow_move_constructible<R>::value
&& std::is_nothrow_move_constructible<D>::value)
try : m_resource(forward_if_nothrow_move_constructible<R>(std::forward<R>(other.m_resource))),
m_deleter(forward_if_nothrow_move_constructible<D>(std::forward<D>(other.m_deleter))),
&& std::is_nothrow_move_constructible<D>::value)
: m_resource(forward_if_nothrow_move_constructible(other.m_resource.get()), make_scope_exit([] { })),
m_deleter(forward_if_nothrow_move_constructible(other.m_deleter.get()), make_scope_exit([&other] {
other.get_deleter()(other.m_resource.get());
other.release(); })),
m_execute_on_destruction(std::exchange(other.m_execute_on_destruction, false)) m_execute_on_destruction(std::exchange(other.m_execute_on_destruction, false))
{ {
} }
catch( ... )
{
other.get_deleter()(other.m_resource);
other.release();
throw;
}




unique_resource(const unique_resource&) = delete; unique_resource(const unique_resource&) = delete;
if( m_execute_on_destruction == true ) if( m_execute_on_destruction == true )
{ {
m_execute_on_destruction = false; m_execute_on_destruction = false;
get_deleter()(m_resource);
get_deleter()(m_resource.get());
} }
} }


{ {
auto se = make_scope_exit([this, &r] { get_deleter()(r); }); auto se = make_scope_exit([this, &r] { get_deleter()(r); });
reset(); reset();
m_resource = move_assign_if_noexcept(r);
m_resource.reset(std::forward<RR>(r));
m_execute_on_destruction = true; m_execute_on_destruction = true;
se.release(); se.release();
} }
m_execute_on_destruction = false; m_execute_on_destruction = false;
} }


const R& get() const noexcept
decltype(auto) get() const noexcept
{ {
return m_resource;
return m_resource.get();
} }


template<class RR = R, template<class RR = R,
> >
RR operator->() const noexcept RR operator->() const noexcept
{ {
return m_resource;
return m_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;
return m_deleter.get();
} }




if( this != &other ) if( this != &other )
{ {
reset(); reset();
m_resource = std::forward<RR>(other.m_resource);
m_deleter = std::forward<DD>(other.m_deleter);

if( std::is_nothrow_move_assignable<R>::value == true )
{
m_deleter.reset(forward_if_nothrow_move_constructible(other.m_deleter.get()));
m_resource.reset(std::forward<RR>(other.m_resource.get()));
}
else if( std::is_nothrow_move_assignable<D>::value == true )
{
m_resource.reset(forward_if_nothrow_move_constructible(other.m_resource.get()));
m_deleter.reset(std::forward<DD>(other.m_deleter.get()));
}
else
{
m_resource = other.m_resource;
m_deleter = other.m_deleter;
}

m_execute_on_destruction = std::exchange(other.m_execute_on_destruction, false); m_execute_on_destruction = std::exchange(other.m_execute_on_destruction, false);
} }
return *this; return *this;


private: private:


R m_resource;
D m_deleter;
Wrapper<R> m_resource;
Wrapper<D> m_deleter;
bool m_execute_on_destruction; bool m_execute_on_destruction;
}; };



Loading…
Cancel
Save