blob: e5e38afbbf42275fe4397ac6c0c423eea480f8b3 [file] [log] [blame]
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <type_traits>
#include <exception>
#include <algorithm>
#include <folly/ExceptionWrapper.h>
#include <folly/Likely.h>
#include <folly/Memory.h>
#include <folly/Portability.h>
#include <folly/futures/FutureException.h>
#include <folly/futures/Unit.h>
namespace folly {
/*
* Try<T> is a wrapper that contains either an instance of T, an exception, or
* nothing. Its primary use case is as a representation of a Promise or Future's
* result and so it provides a number of methods that are useful in that
* context. Exceptions are stored as exception_wrappers so that the user can
* minimize rethrows if so desired.
*
* To represent success or a captured exception, use Try<Unit>
*/
template <class T>
class Try {
static_assert(!std::is_reference<T>::value,
"Try may not be used with reference types");
enum class Contains {
VALUE,
EXCEPTION,
NOTHING,
};
public:
/*
* The value type for the Try
*/
typedef T element_type;
/*
* Construct an empty Try
*/
Try() : contains_(Contains::NOTHING) {}
/*
* Construct a Try with a value by copy
*
* @param v The value to copy in
*/
explicit Try(const T& v) : contains_(Contains::VALUE), value_(v) {}
/*
* Construct a Try with a value by move
*
* @param v The value to move in
*/
explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
/// Implicit conversion from Try<void> to Try<Unit>
template <class T2 = T>
/* implicit */
Try(typename std::enable_if<std::is_same<Unit, T2>::value,
Try<void> const&>::type t);
/*
* Construct a Try with an exception_wrapper
*
* @param e The exception_wrapper
*/
explicit Try(exception_wrapper e)
: contains_(Contains::EXCEPTION),
e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
/*
* DEPRECATED
* Construct a Try with an exception_pointer
*
* @param ep The exception_pointer. Will be rethrown.
*/
FOLLY_DEPRECATED("use Try(exception_wrapper)")
explicit Try(std::exception_ptr ep)
: contains_(Contains::EXCEPTION) {
try {
std::rethrow_exception(ep);
} catch (const std::exception& e) {
e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
} catch (...) {
e_ = folly::make_unique<exception_wrapper>(std::current_exception());
}
}
// Move constructor
Try(Try<T>&& t) noexcept;
// Move assigner
Try& operator=(Try<T>&& t) noexcept;
// Copy constructor
Try(const Try& t);
// Copy assigner
Try& operator=(const Try& t);
~Try();
/*
* Get a mutable reference to the contained value. If the Try contains an
* exception it will be rethrown.
*
* @returns mutable reference to the contained value
*/
T& value();
/*
* Get a const reference to the contained value. If the Try contains an
* exception it will be rethrown.
*
* @returns const reference to the contained value
*/
const T& value() const;
/*
* If the Try contains an exception, rethrow it. Otherwise do nothing.
*/
void throwIfFailed() const;
/*
* Const dereference operator. If the Try contains an exception it will be
* rethrown.
*
* @returns const reference to the contained value
*/
const T& operator*() const { return value(); }
/*
* Dereference operator. If the Try contains an exception it will be rethrown.
*
* @returns mutable reference to the contained value
*/
T& operator*() { return value(); }
/*
* Const arrow operator. If the Try contains an exception it will be
* rethrown.
*
* @returns const reference to the contained value
*/
const T* operator->() const { return &value(); }
/*
* Arrow operator. If the Try contains an exception it will be rethrown.
*
* @returns mutable reference to the contained value
*/
T* operator->() { return &value(); }
/*
* @returns True if the Try contains a value, false otherwise
*/
bool hasValue() const { return contains_ == Contains::VALUE; }
/*
* @returns True if the Try contains an exception, false otherwise
*/
bool hasException() const { return contains_ == Contains::EXCEPTION; }
/*
* @returns True if the Try contains an exception of type Ex, false otherwise
*/
template <class Ex>
bool hasException() const {
return hasException() && e_->is_compatible_with<Ex>();
}
exception_wrapper& exception() {
if (UNLIKELY(!hasException())) {
throw FutureException("exception(): Try does not contain an exception");
}
return *e_;
}
const exception_wrapper& exception() const {
if (UNLIKELY(!hasException())) {
throw FutureException("exception(): Try does not contain an exception");
}
return *e_;
}
/*
* If the Try contains an exception and it is of type Ex, execute func(Ex)
*
* @param func a function that takes a single parameter of type const Ex&
*
* @returns True if the Try held an Ex and func was executed, false otherwise
*/
template <class Ex, class F>
bool withException(F func) const {
if (!hasException()) {
return false;
}
return e_->with_exception<Ex>(std::move(func));
}
template <bool isTry, typename R>
typename std::enable_if<isTry, R>::type get() {
return std::forward<R>(*this);
}
template <bool isTry, typename R>
typename std::enable_if<!isTry, R>::type get() {
return std::forward<R>(value());
}
private:
Contains contains_;
union {
T value_;
std::unique_ptr<exception_wrapper> e_;
};
};
/*
* Specialization of Try for void value type. Encapsulates either success or an
* exception.
*/
template <>
class Try<void> {
public:
// Construct a Try holding a successful and void result
Try() : hasValue_(true) {}
/*
* Construct a Try with an exception_wrapper
*
* @param e The exception_wrapper
*/
explicit Try(exception_wrapper e)
: hasValue_(false),
e_(folly::make_unique<exception_wrapper>(std::move(e))) {}
/*
* DEPRECATED
* Construct a Try with an exception_pointer
*
* @param ep The exception_pointer. Will be rethrown.
*/
FOLLY_DEPRECATED("use Try(exception_wrapper)")
explicit Try(std::exception_ptr ep) : hasValue_(false) {
try {
std::rethrow_exception(ep);
} catch (const std::exception& e) {
e_ = folly::make_unique<exception_wrapper>(std::current_exception(), e);
} catch (...) {
e_ = folly::make_unique<exception_wrapper>(std::current_exception());
}
}
// Copy assigner
Try& operator=(const Try<void>& t) {
hasValue_ = t.hasValue_;
if (t.e_) {
e_ = folly::make_unique<exception_wrapper>(*t.e_);
}
return *this;
}
// Copy constructor
Try(const Try<void>& t) {
*this = t;
}
// If the Try contains an exception, throws it
void value() const { throwIfFailed(); }
// Dereference operator. If the Try contains an exception, throws it
void operator*() const { return value(); }
// If the Try contains an exception, throws it
inline void throwIfFailed() const;
// @returns False if the Try contains an exception, true otherwise
bool hasValue() const { return hasValue_; }
// @returns True if the Try contains an exception, false otherwise
bool hasException() const { return !hasValue_; }
// @returns True if the Try contains an exception of type Ex, false otherwise
template <class Ex>
bool hasException() const {
return hasException() && e_->is_compatible_with<Ex>();
}
/*
* @throws FutureException if the Try doesn't contain an exception
*
* @returns mutable reference to the exception contained by this Try
*/
exception_wrapper& exception() {
if (UNLIKELY(!hasException())) {
throw FutureException("exception(): Try does not contain an exception");
}
return *e_;
}
const exception_wrapper& exception() const {
if (UNLIKELY(!hasException())) {
throw FutureException("exception(): Try does not contain an exception");
}
return *e_;
}
/*
* If the Try contains an exception and it is of type Ex, execute func(Ex)
*
* @param func a function that takes a single parameter of type const Ex&
*
* @returns True if the Try held an Ex and func was executed, false otherwise
*/
template <class Ex, class F>
bool withException(F func) const {
if (!hasException()) {
return false;
}
return e_->with_exception<Ex>(std::move(func));
}
template <bool, typename R>
R get() {
return std::forward<R>(*this);
}
private:
bool hasValue_;
std::unique_ptr<exception_wrapper> e_{nullptr};
};
/*
* Extracts value from try and returns it. Throws if try contained an exception.
*
* @param t Try to extract value from
*
* @returns value contained in t
*/
template <typename T>
T moveFromTry(Try<T>&& t);
/*
* Throws if try contained an exception.
*
* @param t Try to move from
*/
void moveFromTry(Try<void>&& t);
/*
* @param f a function to execute and capture the result of (value or exception)
*
* @returns Try holding the result of f
*/
template <typename F>
typename std::enable_if<
!std::is_same<typename std::result_of<F()>::type, void>::value,
Try<typename std::result_of<F()>::type>>::type
makeTryWith(F&& f);
/*
* Specialization of makeTryWith for void return
*
* @param f a function to execute and capture the result of
*
* @returns Try<void> holding the result of f
*/
template <typename F>
typename std::enable_if<
std::is_same<typename std::result_of<F()>::type, void>::value,
Try<void>>::type
makeTryWith(F&& f);
} // folly
#include <folly/futures/Try-inl.h>