Commit 72cb9df4 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Give Try constructors pedantic exception specifiers

Summary: [Folly] Give Try constructors and assignment operators pedantic exception specifiers deriving from corresponding exception specifiers of element type.

Reviewed By: marshallcline

Differential Revision: D8105158

fbshipit-source-id: 3cc9aae022ddaeef3ef2b742dde8ba084bfca830
parent be791fd5
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
namespace folly { namespace folly {
template <class T> template <class T>
Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) { Try<T>::Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value)
: contains_(t.contains_) {
if (contains_ == Contains::VALUE) { if (contains_ == Contains::VALUE) {
new (&value_)T(std::move(t.value_)); new (&value_)T(std::move(t.value_));
} else if (contains_ == Contains::EXCEPTION) { } else if (contains_ == Contains::EXCEPTION) {
...@@ -34,8 +35,9 @@ Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) { ...@@ -34,8 +35,9 @@ Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) {
template <class T> template <class T>
template <class T2> template <class T2>
Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value, Try<T>::Try(typename std::enable_if<
Try<void> const&>::type t) std::is_same<Unit, T2>::value,
Try<void> const&>::type t) noexcept
: contains_(Contains::NOTHING) { : contains_(Contains::NOTHING) {
if (t.hasValue()) { if (t.hasValue()) {
contains_ = Contains::VALUE; contains_ = Contains::VALUE;
...@@ -47,7 +49,8 @@ Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value, ...@@ -47,7 +49,8 @@ Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value,
} }
template <class T> template <class T>
Try<T>& Try<T>::operator=(Try<T>&& t) noexcept { Try<T>& Try<T>::operator=(Try<T>&& t) noexcept(
std::is_nothrow_move_constructible<T>::value) {
if (this == &t) { if (this == &t) {
return *this; return *this;
} }
...@@ -63,7 +66,8 @@ Try<T>& Try<T>::operator=(Try<T>&& t) noexcept { ...@@ -63,7 +66,8 @@ Try<T>& Try<T>::operator=(Try<T>&& t) noexcept {
} }
template <class T> template <class T>
Try<T>::Try(const Try<T>& t) { Try<T>::Try(const Try<T>& t) noexcept(
std::is_nothrow_copy_constructible<T>::value) {
static_assert( static_assert(
std::is_copy_constructible<T>::value, std::is_copy_constructible<T>::value,
"T must be copyable for Try<T> to be copyable"); "T must be copyable for Try<T> to be copyable");
...@@ -76,7 +80,8 @@ Try<T>::Try(const Try<T>& t) { ...@@ -76,7 +80,8 @@ Try<T>::Try(const Try<T>& t) {
} }
template <class T> template <class T>
Try<T>& Try<T>::operator=(const Try<T>& t) { Try<T>& Try<T>::operator=(const Try<T>& t) noexcept(
std::is_nothrow_copy_constructible<T>::value) {
static_assert( static_assert(
std::is_copy_constructible<T>::value, std::is_copy_constructible<T>::value,
"T must be copyable for Try<T> to be copyable"); "T must be copyable for Try<T> to be copyable");
......
...@@ -67,41 +67,46 @@ class Try { ...@@ -67,41 +67,46 @@ class Try {
/* /*
* Construct an empty Try * Construct an empty Try
*/ */
Try() : contains_(Contains::NOTHING) {} Try() noexcept : contains_(Contains::NOTHING) {}
/* /*
* Construct a Try with a value by copy * Construct a Try with a value by copy
* *
* @param v The value to copy in * @param v The value to copy in
*/ */
explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {} explicit Try(const T& v) noexcept(
std::is_nothrow_copy_constructible<T>::value)
: contains_(Contains::VALUE), value_(v) {}
/* /*
* Construct a Try with a value by move * Construct a Try with a value by move
* *
* @param v The value to move in * @param v The value to move in
*/ */
explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {} explicit Try(T&& v) noexcept(std::is_nothrow_move_constructible<T>::value)
: contains_(Contains::VALUE), value_(std::move(v)) {}
template <typename... Args> template <typename... Args>
explicit Try(in_place_t, Args&&... args) noexcept( explicit Try(in_place_t, Args&&... args) noexcept(
noexcept(::new (nullptr) T(std::declval<Args&&>()...))) std::is_nothrow_constructible<T, Args&&...>::value)
: contains_(Contains::VALUE), value_(std::forward<Args>(args)...) {} : contains_(Contains::VALUE), value_(static_cast<Args&&>(args)...) {}
/// Implicit conversion from Try<void> to Try<Unit> /// Implicit conversion from Try<void> to Try<Unit>
template <class T2 = T> template <class T2 = T>
/* implicit */ /* implicit */
Try(typename std::enable_if<std::is_same<Unit, T2>::value, Try(typename std::enable_if<std::is_same<Unit, T2>::value, Try<void> const&>::
Try<void> const&>::type t); type t) noexcept;
/* /*
* Construct a Try with an exception_wrapper * Construct a Try with an exception_wrapper
* *
* @param e The exception_wrapper * @param e The exception_wrapper
*/ */
explicit Try(exception_wrapper e) explicit Try(exception_wrapper e) noexcept
: contains_(Contains::EXCEPTION), e_(std::move(e)) {} : contains_(Contains::EXCEPTION),
e_(std::move(e)){}
// clang-format off
/* /*
* DEPRECATED * DEPRECATED
* Construct a Try with an exception_pointer * Construct a Try with an exception_pointer
...@@ -109,19 +114,22 @@ class Try { ...@@ -109,19 +114,22 @@ class Try {
* @param ep The exception_pointer. Will be rethrown. * @param ep The exception_pointer. Will be rethrown.
*/ */
[[deprecated("use Try(exception_wrapper)")]] [[deprecated("use Try(exception_wrapper)")]]
explicit Try(std::exception_ptr ep) explicit Try(std::exception_ptr ep) noexcept
: contains_(Contains::EXCEPTION), : contains_(Contains::EXCEPTION),
e_(exception_wrapper::from_exception_ptr(ep)) {} e_(exception_wrapper::from_exception_ptr(ep)) {}
// clang-format on
// Move constructor // Move constructor
Try(Try<T>&& t) noexcept; Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value);
// Move assigner // Move assigner
Try& operator=(Try<T>&& t) noexcept; Try& operator=(Try<T>&& t) noexcept(
std::is_nothrow_move_constructible<T>::value);
// Copy constructor // Copy constructor
Try(const Try& t); Try(const Try& t) noexcept(std::is_nothrow_copy_constructible<T>::value);
// Copy assigner // Copy assigner
Try& operator=(const Try& t); Try& operator=(const Try& t) noexcept(
std::is_nothrow_copy_constructible<T>::value);
~Try(); ~Try();
...@@ -355,33 +363,37 @@ class Try<void> { ...@@ -355,33 +363,37 @@ class Try<void> {
typedef void element_type; typedef void element_type;
// Construct a Try holding a successful and void result // Construct a Try holding a successful and void result
Try() : hasValue_(true) {} Try() noexcept : hasValue_(true) {}
/* /*
* Construct a Try with an exception_wrapper * Construct a Try with an exception_wrapper
* *
* @param e The exception_wrapper * @param e The exception_wrapper
*/ */
explicit Try(exception_wrapper e) : hasValue_(false), e_(std::move(e)) {} explicit Try(exception_wrapper e) noexcept
: hasValue_(false),
e_(std::move(e)){}
// clang-format off
/* /*
* DEPRECATED * DEPRECATED
* Construct a Try with an exception_pointer * Construct a Try with an exception_pointer
* *
* @param ep The exception_pointer. Will be rethrown. * @param ep The exception_pointer. Will be rethrown.
*/ */
[[deprecated("use Try(exception_wrapper)")]] [[deprecated("use Try(exception_wrapper)")]] explicit Try(
explicit Try(std::exception_ptr ep) std::exception_ptr ep) noexcept
: hasValue_(false), e_(exception_wrapper::from_exception_ptr(ep)) {} : hasValue_(false), e_(exception_wrapper::from_exception_ptr(ep)) {}
// clang-format on
// Copy assigner // Copy assigner
Try& operator=(const Try<void>& t) { Try& operator=(const Try<void>& t) noexcept {
hasValue_ = t.hasValue_; hasValue_ = t.hasValue_;
e_ = t.e_; e_ = t.e_;
return *this; return *this;
} }
// Copy constructor // Copy constructor
Try(const Try<void>& t) { Try(const Try<void>& t) noexcept {
*this = t; *this = t;
} }
......
...@@ -37,6 +37,16 @@ class A { ...@@ -37,6 +37,16 @@ class A {
int x_; int x_;
}; };
template <bool Nothrow>
class HasCtors {
public:
explicit HasCtors(int) noexcept(Nothrow) {}
HasCtors(HasCtors&&) noexcept(Nothrow) {}
HasCtors& operator=(HasCtors&&) noexcept(Nothrow) {}
HasCtors(HasCtors const&) noexcept(Nothrow) {}
HasCtors& operator=(HasCtors const&) noexcept(Nothrow) {}
};
class MoveConstructOnly { class MoveConstructOnly {
public: public:
MoveConstructOnly() = default; MoveConstructOnly() = default;
...@@ -71,6 +81,46 @@ TEST(Try, in_place_nested) { ...@@ -71,6 +81,46 @@ TEST(Try, in_place_nested) {
EXPECT_EQ(5, t_t_a.value().value().x()); EXPECT_EQ(5, t_t_a.value().value().x());
} }
TEST(Try, nothrow) {
using F = HasCtors<false>;
using T = HasCtors<true>;
// default ctor
EXPECT_TRUE(std::is_nothrow_default_constructible<Try<F>>::value);
EXPECT_TRUE(std::is_nothrow_default_constructible<Try<T>>::value);
EXPECT_TRUE(std::is_nothrow_default_constructible<Try<void>>::value);
// inner ctor - no void
EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, F&&>::value));
EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, T&&>::value));
EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, F const&>::value));
EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, T const&>::value));
// emplacing ctor - no void
EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, in_place_t, int>::value));
EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, in_place_t, int>::value));
// copy/move ctor/assign
EXPECT_TRUE(std::is_nothrow_constructible<Try<void>>::value);
EXPECT_FALSE(std::is_nothrow_move_constructible<Try<F>>::value);
EXPECT_TRUE(std::is_nothrow_move_constructible<Try<T>>::value);
EXPECT_TRUE(std::is_nothrow_move_constructible<Try<void>>::value);
EXPECT_FALSE(std::is_nothrow_move_assignable<Try<F>>::value);
EXPECT_TRUE(std::is_nothrow_move_assignable<Try<T>>::value);
EXPECT_TRUE(std::is_nothrow_move_assignable<Try<void>>::value);
EXPECT_FALSE(std::is_nothrow_copy_constructible<Try<F>>::value);
EXPECT_TRUE(std::is_nothrow_copy_constructible<Try<T>>::value);
EXPECT_TRUE(std::is_nothrow_copy_constructible<Try<void>>::value);
EXPECT_FALSE(std::is_nothrow_copy_assignable<Try<F>>::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable<Try<T>>::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable<Try<void>>::value);
// conversion ctor - void to unit
EXPECT_TRUE((std::is_nothrow_constructible<Try<Unit>, Try<void>&&>::value));
EXPECT_TRUE(
(std::is_nothrow_constructible<Try<Unit>, Try<void> const&>::value));
}
TEST(Try, MoveDereference) { TEST(Try, MoveDereference) {
auto ptr = std::make_unique<int>(1); auto ptr = std::make_unique<int>(1);
auto t = Try<std::unique_ptr<int>>{std::move(ptr)}; auto t = Try<std::unique_ptr<int>>{std::move(ptr)};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment