handle: Improve noexcept qualification
Now that we are using functors, it should be possible to detect noexcept
state from the Drop / Ref functors and decide the proper noexcept values
for our functions based on that.
Change-Id: I30e7d71bab69a103ee9f713f4f029f4fab0b2dc8
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/stdplus/handle/copyable.hpp b/src/stdplus/handle/copyable.hpp
index a7419d5..86fc8b7 100644
--- a/src/stdplus/handle/copyable.hpp
+++ b/src/stdplus/handle/copyable.hpp
@@ -16,6 +16,10 @@
template <typename Drop, typename Ref>
class HandleF : public Managed<T, As...>::template HandleF<Drop>
{
+ protected:
+ static constexpr bool ref_noexcept =
+ noexcept(Ref()(std::declval<T>(), std::declval<As&>()...));
+
public:
using MHandleF = typename Managed<T, As...>::template HandleF<Drop>;
@@ -24,13 +28,18 @@
* @param[in] maybeV - Optional object being managed
*/
template <typename... Vs>
- constexpr explicit HandleF(const std::optional<T>& maybeV, Vs&&... vs) :
+ constexpr explicit HandleF(const std::optional<T>& maybeV, Vs&&... vs) noexcept(
+ noexcept(MHandleF(std::nullopt, std::declval<Vs>()...)) && noexcept(
+ std::declval<HandleF>().reset(
+ std::declval<const std::optional<T>&>()))) :
MHandleF(std::nullopt, std::forward<Vs>(vs)...)
{
reset(maybeV);
}
template <typename... Vs>
- constexpr explicit HandleF(const T& maybeV, Vs&&... vs) :
+ constexpr explicit HandleF(const T& maybeV, Vs&&... vs) noexcept(
+ noexcept(MHandleF(std::nullopt, std::declval<Vs>()...)) && noexcept(
+ std::declval<HandleF>().reset(std::declval<const T&>()))) :
MHandleF(std::nullopt, std::forward<Vs>(vs)...)
{
reset(maybeV);
@@ -41,17 +50,29 @@
* @param[in] maybeV - Maybe the object being managed
*/
template <typename... Vs>
- constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
+ constexpr explicit HandleF(
+ std::optional<T>&& maybeV,
+ Vs&&... vs) noexcept(noexcept(MHandleF(std::
+ declval<std::optional<
+ T>&&>(),
+ std::declval<Vs>()...))) :
MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
{
}
template <typename... Vs>
- constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
+ constexpr explicit HandleF(T&& maybeV, Vs&&... vs) noexcept(
+ noexcept(MHandleF(std::declval<T&&>(), std::declval<Vs>()...))) :
MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
{
}
- constexpr HandleF(const HandleF& other) :
+ constexpr HandleF(const HandleF& other) noexcept(noexcept(MHandleF(
+ std::nullopt,
+ std::declval<const std::tuple<
+ As...>&>())) && noexcept(std::declval<HandleF>()
+ .reset(std::declval<
+ const std::optional<
+ T>&>()))) :
MHandleF(std::nullopt, other.as)
{
reset(other.maybe_value());
@@ -63,7 +84,11 @@
{
}
- constexpr HandleF& operator=(const HandleF& other)
+ constexpr HandleF& operator=(const HandleF& other) noexcept(
+ noexcept(std::declval<HandleF>().reset()) &&
+ std::is_nothrow_copy_constructible_v<std::tuple<As...>>&& noexcept(
+ std::declval<HandleF>().reset(
+ std::declval<const std::optional<T>&>())))
{
if (this != &other)
{
@@ -74,20 +99,18 @@
return *this;
}
- constexpr HandleF& operator=(HandleF&& other)
+ constexpr HandleF& operator=(HandleF&& other) noexcept(
+ std::is_nothrow_move_assignable_v<MHandleF>)
{
MHandleF::operator=(std::move(other));
return *this;
}
using MHandleF::reset;
-
- /** @brief Resets the managed value to a new value
- * Takes a new reference on the value
- *
- * @param[in] maybeV - Maybe the new value
- */
- constexpr void reset(const std::optional<T>& maybeV)
+ constexpr void reset(const std::optional<T>& maybeV) noexcept(
+ ref_noexcept&& noexcept(std::declval<HandleF>().reset(
+ std::declval<T>())) && noexcept(std::declval<HandleF>()
+ .reset()))
{
if (maybeV)
{
@@ -95,17 +118,19 @@
}
else
{
- reset(std::nullopt);
+ reset();
}
}
- constexpr void reset(const T& maybeV)
+ constexpr void reset(const T& maybeV) noexcept(ref_noexcept&& noexcept(
+ std::declval<HandleF>().reset(std::declval<T>())))
{
reset(doRef(maybeV, std::index_sequence_for<As...>()));
}
private:
template <size_t... Indices>
- T doRef(const T& v, std::index_sequence<Indices...>)
+ T doRef(const T& v,
+ std::index_sequence<Indices...>) noexcept(ref_noexcept)
{
return Ref()(v, std::get<Indices>(this->as)...);
}
diff --git a/src/stdplus/handle/managed.hpp b/src/stdplus/handle/managed.hpp
index df4a142..08cac4f 100644
--- a/src/stdplus/handle/managed.hpp
+++ b/src/stdplus/handle/managed.hpp
@@ -35,19 +35,29 @@
template <typename Drop>
class HandleF
{
+ protected:
+ static constexpr bool drop_noexcept =
+ noexcept(Drop()(std::declval<T>(), std::declval<As&>()...));
+
public:
/** @brief Creates a handle owning the object
*
* @param[in] maybeV - Maybe the object being managed
*/
template <typename... Vs>
- constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
- as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
+ constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) noexcept(
+ std::is_nothrow_move_constructible_v<std::optional<T>>&& noexcept(
+ std::tuple<As...>(std::declval<Vs>()...))) :
+ as(std::forward<Vs>(vs)...),
+ maybeT(std::move(maybeV))
{
}
template <typename... Vs>
- constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
- as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
+ constexpr explicit HandleF(T&& maybeV, Vs&&... vs) noexcept(
+ std::is_nothrow_move_constructible_v<std::optional<T>>&& noexcept(
+ std::tuple<As...>(std::declval<Vs>()...))) :
+ as(std::forward<Vs>(vs)...),
+ maybeT(std::move(maybeV))
{
}
@@ -63,7 +73,10 @@
other.maybeT = std::nullopt;
}
- constexpr HandleF& operator=(HandleF&& other)
+ constexpr HandleF& operator=(HandleF&& other) noexcept(
+ std::is_nothrow_move_assignable_v<std::tuple<As...>>&& noexcept(
+ std::declval<HandleF>().reset(
+ std::declval<std::optional<T>>())))
{
if (this != &other)
{
@@ -74,16 +87,12 @@
return *this;
}
- virtual ~HandleF()
+ virtual ~HandleF() noexcept(
+ std::is_nothrow_destructible_v<std::tuple<As...>>&&
+ std::is_nothrow_destructible_v<std::optional<T>>&& noexcept(
+ std::declval<HandleF>().reset()))
{
- try
- {
- reset();
- }
- catch (...)
- {
- std::abort();
- }
+ reset();
}
/** @brief Gets the managed object
@@ -147,12 +156,14 @@
*
* @param[in] maybeV - Maybe the new value
*/
- constexpr void reset(std::optional<T>&& maybeV)
+ constexpr void reset(std::optional<T>&& maybeV) noexcept(
+ drop_noexcept&& std::is_nothrow_move_assignable_v<std::optional<T>>)
{
maybeDrop(std::index_sequence_for<As...>());
maybeT = std::move(maybeV);
}
- constexpr void reset(T&& maybeV)
+ constexpr void reset(T&& maybeV) noexcept(
+ drop_noexcept&& std::is_nothrow_move_assignable_v<std::optional<T>>)
{
maybeDrop(std::index_sequence_for<As...>());
maybeT = std::move(maybeV);
@@ -161,7 +172,7 @@
/** @brief A shorthand reset function for convenience
* Same as calling reset(std::nullopt)
*/
- constexpr void reset()
+ constexpr void reset() noexcept(drop_noexcept)
{
reset(std::nullopt);
}
@@ -213,7 +224,7 @@
std::optional<T> maybeT;
template <size_t... Indices>
- void maybeDrop(std::index_sequence<Indices...>)
+ void maybeDrop(std::index_sequence<Indices...>) noexcept(drop_noexcept)
{
if (maybeT)
{
diff --git a/test/handle/copyable.cpp b/test/handle/copyable.cpp
index b55b216..cd17c54 100644
--- a/test/handle/copyable.cpp
+++ b/test/handle/copyable.cpp
@@ -68,6 +68,18 @@
using StoreHandle =
Copyable<int, std::string, int>::HandleF<StoreDrop, StoreRef>;
+static_assert(std::is_nothrow_copy_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>);
+static_assert(std::is_nothrow_destructible_v<SimpleHandle>);
+static_assert(noexcept(std::declval<SimpleHandle>().reset()));
+static_assert(!std::is_nothrow_copy_constructible_v<StoreHandle>);
+// http://cplusplus.github.io/LWG/lwg-active.html#2116
+// static_assert(std::is_nothrow_move_constructible_v<StoreHandle>);
+static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>);
+static_assert(!std::is_nothrow_destructible_v<StoreHandle>);
+static_assert(!noexcept(std::declval<StoreHandle>().reset()));
+
class CopyableHandleTest : public ::testing::Test
{
protected:
diff --git a/test/handle/managed.cpp b/test/handle/managed.cpp
index fffffde..fb89195 100644
--- a/test/handle/managed.cpp
+++ b/test/handle/managed.cpp
@@ -41,6 +41,16 @@
using SimpleHandle = Managed<int>::HandleF<SimpleDrop>;
using StoreHandle = Managed<int, std::string, int>::HandleF<StoreDrop>;
+static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>);
+static_assert(std::is_nothrow_destructible_v<SimpleHandle>);
+static_assert(noexcept(std::declval<SimpleHandle>().reset()));
+// http://cplusplus.github.io/LWG/lwg-active.html#2116
+// static_assert(std::is_nothrow_move_constructible_v<StoreHandle>);
+static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>);
+static_assert(!std::is_nothrow_destructible_v<StoreHandle>);
+static_assert(!noexcept(std::declval<StoreHandle>().reset()));
+
class ManagedHandleTest : public ::testing::Test
{
protected: