| 1 | /* |
| 2 | * Copyright 2014-present Facebook, Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | #include <folly/Utility.h> |
| 20 | #include <folly/functional/Invoke.h> |
| 21 | |
| 22 | #include <stdexcept> |
| 23 | #include <tuple> |
| 24 | |
| 25 | namespace folly { |
| 26 | |
| 27 | template <class T> |
| 28 | Try<T>::Try(Try<T>&& t) noexcept(std::is_nothrow_move_constructible<T>::value) |
| 29 | : contains_(t.contains_) { |
| 30 | if (contains_ == Contains::VALUE) { |
| 31 | new (&value_) T(std::move(t.value_)); |
| 32 | } else if (contains_ == Contains::EXCEPTION) { |
| 33 | new (&e_) exception_wrapper(std::move(t.e_)); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | template <class T> |
| 38 | template <class T2> |
| 39 | Try<T>::Try(typename std::enable_if< |
| 40 | std::is_same<Unit, T2>::value, |
| 41 | Try<void> const&>::type t) noexcept |
| 42 | : contains_(Contains::NOTHING) { |
| 43 | if (t.hasValue()) { |
| 44 | contains_ = Contains::VALUE; |
| 45 | new (&value_) T(); |
| 46 | } else if (t.hasException()) { |
| 47 | contains_ = Contains::EXCEPTION; |
| 48 | new (&e_) exception_wrapper(t.exception()); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | template <class T> |
| 53 | Try<T>& Try<T>::operator=(Try<T>&& t) noexcept( |
| 54 | std::is_nothrow_move_constructible<T>::value) { |
| 55 | if (this == &t) { |
| 56 | return *this; |
| 57 | } |
| 58 | |
| 59 | destroy(); |
| 60 | |
| 61 | if (t.contains_ == Contains::VALUE) { |
| 62 | new (&value_) T(std::move(t.value_)); |
| 63 | } else if (t.contains_ == Contains::EXCEPTION) { |
| 64 | new (&e_) exception_wrapper(std::move(t.e_)); |
| 65 | } |
| 66 | |
| 67 | contains_ = t.contains_; |
| 68 | |
| 69 | return *this; |
| 70 | } |
| 71 | |
| 72 | template <class T> |
| 73 | Try<T>::Try(const Try<T>& t) noexcept( |
| 74 | std::is_nothrow_copy_constructible<T>::value) { |
| 75 | static_assert( |
| 76 | std::is_copy_constructible<T>::value, |
| 77 | "T must be copyable for Try<T> to be copyable" ); |
| 78 | contains_ = t.contains_; |
| 79 | if (contains_ == Contains::VALUE) { |
| 80 | new (&value_) T(t.value_); |
| 81 | } else if (contains_ == Contains::EXCEPTION) { |
| 82 | new (&e_) exception_wrapper(t.e_); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | template <class T> |
| 87 | Try<T>& Try<T>::operator=(const Try<T>& t) noexcept( |
| 88 | std::is_nothrow_copy_constructible<T>::value) { |
| 89 | static_assert( |
| 90 | std::is_copy_constructible<T>::value, |
| 91 | "T must be copyable for Try<T> to be copyable" ); |
| 92 | |
| 93 | if (this == &t) { |
| 94 | return *this; |
| 95 | } |
| 96 | |
| 97 | destroy(); |
| 98 | |
| 99 | if (t.contains_ == Contains::VALUE) { |
| 100 | new (&value_) T(t.value_); |
| 101 | } else if (t.contains_ == Contains::EXCEPTION) { |
| 102 | new (&e_) exception_wrapper(t.e_); |
| 103 | } |
| 104 | |
| 105 | contains_ = t.contains_; |
| 106 | |
| 107 | return *this; |
| 108 | } |
| 109 | |
| 110 | template <class T> |
| 111 | Try<T>::~Try() { |
| 112 | if (LIKELY(contains_ == Contains::VALUE)) { |
| 113 | value_.~T(); |
| 114 | } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) { |
| 115 | e_.~exception_wrapper(); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | template <typename T> |
| 120 | template <typename... Args> |
| 121 | T& Try<T>::emplace(Args&&... args) noexcept( |
| 122 | std::is_nothrow_constructible<T, Args&&...>::value) { |
| 123 | this->destroy(); |
| 124 | new (&value_) T(static_cast<Args&&>(args)...); |
| 125 | contains_ = Contains::VALUE; |
| 126 | return value_; |
| 127 | } |
| 128 | |
| 129 | template <typename T> |
| 130 | template <typename... Args> |
| 131 | exception_wrapper& Try<T>::emplaceException(Args&&... args) noexcept( |
| 132 | std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { |
| 133 | this->destroy(); |
| 134 | new (&e_) exception_wrapper(static_cast<Args&&>(args)...); |
| 135 | contains_ = Contains::EXCEPTION; |
| 136 | return e_; |
| 137 | } |
| 138 | |
| 139 | template <class T> |
| 140 | T& Try<T>::value() & { |
| 141 | throwIfFailed(); |
| 142 | return value_; |
| 143 | } |
| 144 | |
| 145 | template <class T> |
| 146 | T&& Try<T>::value() && { |
| 147 | throwIfFailed(); |
| 148 | return std::move(value_); |
| 149 | } |
| 150 | |
| 151 | template <class T> |
| 152 | const T& Try<T>::value() const& { |
| 153 | throwIfFailed(); |
| 154 | return value_; |
| 155 | } |
| 156 | |
| 157 | template <class T> |
| 158 | const T&& Try<T>::value() const&& { |
| 159 | throwIfFailed(); |
| 160 | return std::move(value_); |
| 161 | } |
| 162 | |
| 163 | template <class T> |
| 164 | void Try<T>::throwIfFailed() const { |
| 165 | switch (contains_) { |
| 166 | case Contains::VALUE: |
| 167 | return; |
| 168 | case Contains::EXCEPTION: |
| 169 | e_.throw_exception(); |
| 170 | default: |
| 171 | throw_exception<UsingUninitializedTry>(); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | template <class T> |
| 176 | void Try<T>::destroy() noexcept { |
| 177 | auto oldContains = folly::exchange(contains_, Contains::NOTHING); |
| 178 | if (LIKELY(oldContains == Contains::VALUE)) { |
| 179 | value_.~T(); |
| 180 | } else if (UNLIKELY(oldContains == Contains::EXCEPTION)) { |
| 181 | e_.~exception_wrapper(); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | Try<void>& Try<void>::operator=(const Try<void>& t) noexcept { |
| 186 | if (t.hasException()) { |
| 187 | if (hasException()) { |
| 188 | e_ = t.e_; |
| 189 | } else { |
| 190 | new (&e_) exception_wrapper(t.e_); |
| 191 | hasValue_ = false; |
| 192 | } |
| 193 | } else { |
| 194 | if (hasException()) { |
| 195 | e_.~exception_wrapper(); |
| 196 | hasValue_ = true; |
| 197 | } |
| 198 | } |
| 199 | return *this; |
| 200 | } |
| 201 | |
| 202 | template <typename... Args> |
| 203 | exception_wrapper& Try<void>::emplaceException(Args&&... args) noexcept( |
| 204 | std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) { |
| 205 | if (hasException()) { |
| 206 | e_.~exception_wrapper(); |
| 207 | } |
| 208 | new (&e_) exception_wrapper(static_cast<Args&&>(args)...); |
| 209 | hasValue_ = false; |
| 210 | return e_; |
| 211 | } |
| 212 | |
| 213 | void Try<void>::throwIfFailed() const { |
| 214 | if (hasException()) { |
| 215 | e_.throw_exception(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | template <typename F> |
| 220 | typename std::enable_if< |
| 221 | !std::is_same<invoke_result_t<F>, void>::value, |
| 222 | Try<invoke_result_t<F>>>::type |
| 223 | makeTryWith(F&& f) { |
| 224 | using ResultType = invoke_result_t<F>; |
| 225 | try { |
| 226 | return Try<ResultType>(f()); |
| 227 | } catch (std::exception& e) { |
| 228 | return Try<ResultType>(exception_wrapper(std::current_exception(), e)); |
| 229 | } catch (...) { |
| 230 | return Try<ResultType>(exception_wrapper(std::current_exception())); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | template <typename F> |
| 235 | typename std:: |
| 236 | enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type |
| 237 | makeTryWith(F&& f) { |
| 238 | try { |
| 239 | f(); |
| 240 | return Try<void>(); |
| 241 | } catch (std::exception& e) { |
| 242 | return Try<void>(exception_wrapper(std::current_exception(), e)); |
| 243 | } catch (...) { |
| 244 | return Try<void>(exception_wrapper(std::current_exception())); |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | template <typename T, typename... Args> |
| 249 | T* tryEmplace(Try<T>& t, Args&&... args) noexcept { |
| 250 | try { |
| 251 | return std::addressof(t.emplace(static_cast<Args&&>(args)...)); |
| 252 | } catch (const std::exception& ex) { |
| 253 | t.emplaceException(std::current_exception(), ex); |
| 254 | return nullptr; |
| 255 | } catch (...) { |
| 256 | t.emplaceException(std::current_exception()); |
| 257 | return nullptr; |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | void tryEmplace(Try<void>& t) noexcept { |
| 262 | t.emplace(); |
| 263 | } |
| 264 | |
| 265 | template <typename T, typename Func> |
| 266 | T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept { |
| 267 | static_assert( |
| 268 | std::is_constructible<T, folly::invoke_result_t<Func>>::value, |
| 269 | "Unable to initialise a value of type T with the result of 'func'" ); |
| 270 | try { |
| 271 | return std::addressof(t.emplace(static_cast<Func&&>(func)())); |
| 272 | } catch (const std::exception& ex) { |
| 273 | t.emplaceException(std::current_exception(), ex); |
| 274 | return nullptr; |
| 275 | } catch (...) { |
| 276 | t.emplaceException(std::current_exception()); |
| 277 | return nullptr; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | template <typename Func> |
| 282 | bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept { |
| 283 | static_assert( |
| 284 | std::is_void<folly::invoke_result_t<Func>>::value, |
| 285 | "Func returns non-void. Cannot be used to emplace Try<void>" ); |
| 286 | try { |
| 287 | static_cast<Func&&>(func)(); |
| 288 | t.emplace(); |
| 289 | return true; |
| 290 | } catch (const std::exception& ex) { |
| 291 | t.emplaceException(std::current_exception(), ex); |
| 292 | return false; |
| 293 | } catch (...) { |
| 294 | t.emplaceException(std::current_exception()); |
| 295 | return false; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | namespace try_detail { |
| 300 | |
| 301 | /** |
| 302 | * Trait that removes the layer of Try abstractions from the passed in type |
| 303 | */ |
| 304 | template <typename Type> |
| 305 | struct RemoveTry; |
| 306 | template <template <typename...> class TupleType, typename... Types> |
| 307 | struct RemoveTry<TupleType<folly::Try<Types>...>> { |
| 308 | using type = TupleType<Types...>; |
| 309 | }; |
| 310 | |
| 311 | template <std::size_t... Indices, typename Tuple> |
| 312 | auto unwrapTryTupleImpl(folly::index_sequence<Indices...>, Tuple&& instance) { |
| 313 | using std::get; |
| 314 | using ReturnType = typename RemoveTry<typename std::decay<Tuple>::type>::type; |
| 315 | return ReturnType{(get<Indices>(std::forward<Tuple>(instance)).value())...}; |
| 316 | } |
| 317 | } // namespace try_detail |
| 318 | |
| 319 | template <typename Tuple> |
| 320 | auto unwrapTryTuple(Tuple&& instance) { |
| 321 | using TupleDecayed = typename std::decay<Tuple>::type; |
| 322 | using Seq = folly::make_index_sequence<std::tuple_size<TupleDecayed>::value>; |
| 323 | return try_detail::unwrapTryTupleImpl(Seq{}, std::forward<Tuple>(instance)); |
| 324 | } |
| 325 | |
| 326 | } // namespace folly |
| 327 | |