/* * Copyright 2016 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 #include #include #include #include #include #include #include namespace folly { class TryException : public std::exception { public: explicit TryException(std::string message_arg) noexcept : message(std::move(message_arg)) {} const char* what() const noexcept override { return message.c_str(); } bool operator==(const TryException& other) const noexcept { return other.message == this->message; } bool operator!=(const TryException& other) const noexcept { return !(*this == other); } protected: std::string message; }; class UsingUninitializedTry : public TryException { public: UsingUninitializedTry() noexcept : TryException("Using unitialized try") {} }; /* * Try is a wrapper that contains either an instance of T, an exception, or * nothing. 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. */ template class Try { static_assert(!std::is_reference::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 to Try template /* implicit */ Try(typename std::enable_if::value, Try 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(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(std::current_exception(), e); } catch (...) { e_ = folly::make_unique(std::current_exception()); } } // Move constructor Try(Try&& t) noexcept; // Move assigner Try& operator=(Try&& 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 rvalue reference to the contained value. If the Try contains an * exception it will be rethrown. * * @returns rvalue 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 bool hasException() const { return hasException() && e_->is_compatible_with(); } exception_wrapper& exception() { if (UNLIKELY(!hasException())) { throw TryException("exception(): Try does not contain an exception"); } return *e_; } const exception_wrapper& exception() const { if (UNLIKELY(!hasException())) { throw TryException("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 bool withException(F func) const { if (!hasException()) { return false; } return e_->with_exception(std::move(func)); } template typename std::enable_if::type get() { return std::forward(*this); } template typename std::enable_if::type get() { return std::forward(value()); } private: Contains contains_; union { T value_; std::unique_ptr e_; }; }; /* * Specialization of Try for void value type. Encapsulates either success or an * exception. */ template <> class Try { public: /* * The value type for the Try */ typedef void element_type; // 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(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(std::current_exception(), e); } catch (...) { e_ = folly::make_unique(std::current_exception()); } } // Copy assigner Try& operator=(const Try& t) { hasValue_ = t.hasValue_; if (t.e_) { e_ = folly::make_unique(*t.e_); } return *this; } // Copy constructor Try(const Try& 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 bool hasException() const { return hasException() && e_->is_compatible_with(); } /* * @throws TryException 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 TryException("exception(): Try does not contain an exception"); } return *e_; } const exception_wrapper& exception() const { if (UNLIKELY(!hasException())) { throw TryException("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 bool withException(F func) const { if (!hasException()) { return false; } return e_->with_exception(std::move(func)); } template R get() { return std::forward(*this); } private: bool hasValue_; std::unique_ptr 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 T moveFromTry(Try& t); /* * Throws if try contained an exception. * * @param t Try to move from */ void moveFromTry(Try& t); /* * @param f a function to execute and capture the result of (value or exception) * * @returns Try holding the result of f */ template typename std::enable_if< !std::is_same::type, void>::value, Try::type>>::type makeTryWith(F&& f); /* * Specialization of makeTryWith for void return * * @param f a function to execute and capture the result of * * @returns Try holding the result of f */ template typename std::enable_if< std::is_same::type, void>::value, Try>::type makeTryWith(F&& f); template std::tuple unwrapTryTuple(std::tuple...>&& ts); } // folly #include