folly/folly/Try.h

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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 <exception>
#include <stdexcept>
#include <type_traits>
#include <utility>

#include <folly/ExceptionWrapper.h>
#include <folly/Likely.h>
#include <folly/Memory.h>
#include <folly/Portability.h>
#include <folly/Unit.h>
#include <folly/Utility.h>
#include <folly/functional/Invoke.h>
#include <folly/lang/Exception.h>

namespace folly {

class FOLLY_EXPORT TryException : public std::logic_error {};

class FOLLY_EXPORT UsingUninitializedTry : public TryException {};

template <class T>
class Try;

namespace detail {
template <class T>
class TryBase {};
} // namespace detail

/*
 * Try<T> 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<Unit>.
 */
template <class T>
class Try : detail::TryBase<T>,
            moveonly_::EnableCopyMove<
                std::is_copy_constructible<T>::value,
                std::is_move_constructible<T>::value> {};

/*
 * Specialization of Try for void value type. Encapsulates either success or an
 * exception.
 */
template <>
class Try<void> {};

template <typename T>
struct isTry : std::false_type {};

isTry<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 F>
typename std::enable_if<
    !std::is_same<invoke_result_t<F>, void>::value,
    Try<invoke_result_t<F>>>::type
makeTryWithNoUnwrap(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<invoke_result_t<F>, void>::value, Try<void>>::type
    makeTryWithNoUnwrap(F&& f);

/*
 * @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<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type
    makeTryWith(F&& f);

/*
 * Specialization of makeTryWith for functions that return Try<T>
 * Causes makeTryWith to not double-wrap the try.
 *
 * @param f a function to execute and capture the result of
 *
 * @returns result of f if f did not throw. Otherwise Try<T> containing
 * exception
 */
template <typename F>
typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>::
    type
    makeTryWith(F&& f);

/*
 * Try to in-place construct a new value from the specified arguments.
 *
 * If T's constructor throws an exception then this is caught and the Try<T>
 * object is initialised to hold that exception.
 *
 * @param args Are passed to T's constructor.
 */
template <typename T, typename... Args>
T* tryEmplace(Try<T>& t, Args&&... args) noexcept;

/*
 * Overload of tryEmplace() for Try<void>.
 */
inline void tryEmplace(Try<void>& t) noexcept;

/*
 * Try to in-place construct a new value from the result of a function.
 *
 * If the function completes successfully then attempts to in-place construct
 * a value of type, T, passing the result of the function as the only parameter.
 *
 * If either the call to the function completes with an exception or the
 * constructor completes with an exception then the exception is caught and
 * stored in the Try object.
 *
 * @returns A pointer to the newly constructed object if it completed
 * successfully, otherwise returns nullptr if the operation completed with
 * an exception.
 */
template <typename T, typename Func>
T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept;

/*
 * Specialization of tryEmplaceWith() for Try<void>.
 *
 * Calls func() and if it doesn't throw an exception then calls t.emplace().
 * If func() throws then captures the exception in t using t.emplaceException().
 *
 * Func must be callable with zero parameters and must return void.
 *
 * @returns true if func() completed without an exception, false if func()
 * threw an exception.
 */
template <typename Func>
bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept;

/**
 * Tuple<Try<Type>...> -> std::tuple<Type...>
 *
 * Unwraps a tuple-like type containing a sequence of Try<Type> instances to
 * std::tuple<Type>
 */
template <typename Tuple>
auto unwrapTryTuple(Tuple&&);

/*
 * Try to move the value/exception from another Try object.
 *
 * If T's constructor throws an exception then this is caught and the Try<T>
 * object is initialised to hold that exception.
 */
template <typename T>
void tryAssign(Try<T>& t, Try<T>&& other) noexcept;

template <typename T>
Try(T&&) -> Try<std::decay_t<T>>;

} // namespace folly

#include <folly/Try-inl.h>