1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 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 <exception> 20 #include <stdexcept> 21 #include <type_traits> 22 #include <utility> 23 24 #include <folly/ExceptionWrapper.h> 25 #include <folly/Likely.h> 26 #include <folly/Memory.h> 27 #include <folly/Portability.h> 28 #include <folly/Unit.h> 29 #include <folly/Utility.h> 30 #include <folly/functional/Invoke.h> 31 #include <folly/lang/Exception.h> 32 33 namespace folly { 34 35 class FOLLY_EXPORT TryException : public std::logic_error { 36 public: 37 using std::logic_error::logic_error; 38 }; 39 40 class FOLLY_EXPORT UsingUninitializedTry : public TryException { 41 public: UsingUninitializedTry()42 UsingUninitializedTry() : TryException("Using uninitialized try") {} 43 }; 44 45 template <class T> 46 class Try; 47 48 namespace detail { 49 template <class T> 50 class TryBase { 51 protected: 52 enum class Contains { 53 VALUE, 54 EXCEPTION, 55 NOTHING, 56 }; 57 58 public: 59 /* 60 * Construct an empty Try 61 */ TryBase()62 TryBase() noexcept : contains_(Contains::NOTHING) {} 63 64 /* 65 * Construct a Try with a value by copy 66 * 67 * @param v The value to copy in 68 */ TryBase(const T & v)69 explicit TryBase(const T& v) noexcept( 70 std::is_nothrow_copy_constructible<T>::value) 71 : contains_(Contains::VALUE), value_(v) {} 72 73 /* 74 * Construct a Try with a value by move 75 * 76 * @param v The value to move in 77 */ TryBase(T && v)78 explicit TryBase(T&& v) noexcept(std::is_nothrow_move_constructible<T>::value) 79 : contains_(Contains::VALUE), value_(std::move(v)) {} 80 81 template <typename... Args> TryBase(in_place_t,Args &&...args)82 explicit TryBase(in_place_t, Args&&... args) noexcept( 83 std::is_nothrow_constructible<T, Args&&...>::value) 84 : contains_(Contains::VALUE), value_(static_cast<Args&&>(args)...) {} 85 86 /// Implicit conversion from Try<void> to Try<Unit> 87 template <class T2 = T> 88 /* implicit */ TryBase(typename std::enable_if< 89 std::is_same<Unit, T2>::value, 90 Try<void> const&>::type t) noexcept; 91 92 /* 93 * Construct a Try with an exception_wrapper 94 * 95 * @param e The exception_wrapper 96 */ TryBase(exception_wrapper e)97 explicit TryBase(exception_wrapper e) noexcept 98 : contains_(Contains::EXCEPTION), e_(std::move(e)) {} 99 100 ~TryBase(); 101 102 // Move constructor 103 TryBase(TryBase&& t) noexcept(std::is_nothrow_move_constructible<T>::value); 104 // Move assigner 105 TryBase& operator=(TryBase&& t) noexcept( 106 std::is_nothrow_move_constructible<T>::value); 107 108 // Copy constructor 109 TryBase(const TryBase& t) noexcept( 110 std::is_nothrow_copy_constructible<T>::value); 111 // Copy assigner 112 TryBase& operator=(const TryBase& t) noexcept( 113 std::is_nothrow_copy_constructible<T>::value); 114 115 protected: 116 void destroy() noexcept; 117 118 Contains contains_; 119 union { 120 T value_; 121 exception_wrapper e_; 122 }; 123 }; 124 } // namespace detail 125 126 /* 127 * Try<T> is a wrapper that contains either an instance of T, an exception, or 128 * nothing. Exceptions are stored as exception_wrappers so that the user can 129 * minimize rethrows if so desired. 130 * 131 * To represent success or a captured exception, use Try<Unit>. 132 */ 133 template <class T> 134 class Try : detail::TryBase<T>, 135 moveonly_::EnableCopyMove< 136 std::is_copy_constructible<T>::value, 137 std::is_move_constructible<T>::value> { 138 static_assert( 139 !std::is_reference<T>::value, "Try may not be used with reference types"); 140 using typename detail::TryBase<T>::Contains; 141 142 public: 143 using detail::TryBase<T>::TryBase; 144 145 /* 146 * The value type for the Try 147 */ 148 using element_type = T; 149 150 /* 151 * In-place construct the value in the Try object. 152 * 153 * Destroys any previous value prior to constructing the new value. 154 * Leaves *this in an empty state if the construction of T throws. 155 * 156 * @returns reference to the newly constructed value. 157 */ 158 template <typename... Args> 159 T& emplace(Args&&... args) noexcept( 160 std::is_nothrow_constructible<T, Args&&...>::value); 161 162 /* 163 * In-place construct an exception in the Try object. 164 * 165 * Destroys any previous value prior to constructing the new value. 166 * Leaves *this in an empty state if the construction of the exception_wrapper 167 * throws. 168 * 169 * Any arguments passed to emplaceException() are forwarded on to the 170 * exception_wrapper constructor. 171 * 172 * @returns reference to the newly constructed exception_wrapper. 173 */ 174 template <typename... Args> 175 exception_wrapper& emplaceException(Args&&... args) noexcept( 176 std::is_nothrow_constructible<exception_wrapper, Args&&...>::value); 177 178 /* 179 * Get a mutable reference to the contained value. 180 * [Re]throws if the Try contains an exception or is empty. 181 * 182 * @returns mutable reference to the contained value 183 */ 184 T& value() &; 185 /* 186 * Get a rvalue reference to the contained value. 187 * [Re]throws if the Try contains an exception or is empty. 188 * 189 * @returns rvalue reference to the contained value 190 */ 191 T&& value() &&; 192 /* 193 * Get a const reference to the contained value. 194 * [Re]throws if the Try contains an exception or is empty. 195 * 196 * @returns const reference to the contained value 197 */ 198 const T& value() const&; 199 /* 200 * Get a const rvalue reference to the contained value. 201 * [Re]throws if the Try contains an exception or is empty. 202 * 203 * @returns const rvalue reference to the contained value 204 */ 205 const T&& value() const&&; 206 207 /* 208 * Returns a copy of the contained value if *this has a value, 209 * otherwise returns a value constructed from defaultValue. 210 * 211 * The selected constructor of the return value may throw exceptions. 212 */ 213 template <class U> 214 T value_or(U&& defaultValue) const&; 215 template <class U> 216 T value_or(U&& defaultValue) &&; 217 218 /* 219 * [Re]throw if the Try contains an exception or is empty. Otherwise do 220 * nothing. 221 */ 222 void throwUnlessValue() const; 223 [[deprecated("Replaced by throwUnlessValue")]] void throwIfFailed() const; 224 225 /* 226 * Const dereference operator. 227 * [Re]throws if the Try contains an exception or is empty. 228 * 229 * @returns const reference to the contained value 230 */ 231 const T& operator*() const& { return value(); } 232 /* 233 * Dereference operator. If the Try contains an exception it will be rethrown. 234 * 235 * @returns mutable reference to the contained value 236 */ 237 T& operator*() & { return value(); } 238 /* 239 * Mutable rvalue dereference operator. If the Try contains an exception it 240 * will be rethrown. 241 * 242 * @returns rvalue reference to the contained value 243 */ 244 T&& operator*() && { return std::move(value()); } 245 /* 246 * Const rvalue dereference operator. If the Try contains an exception it 247 * will be rethrown. 248 * 249 * @returns rvalue reference to the contained value 250 */ 251 const T&& operator*() const&& { return std::move(value()); } 252 253 /* 254 * Const arrow operator. 255 * [Re]throws if the Try contains an exception or is empty. 256 * 257 * @returns const reference to the contained value 258 */ 259 const T* operator->() const { return &value(); } 260 /* 261 * Arrow operator. If the Try contains an exception it will be rethrown. 262 * 263 * @returns mutable reference to the contained value 264 */ 265 T* operator->() { return &value(); } 266 267 /* 268 * @returns True if the Try contains a value, false otherwise 269 */ hasValue()270 bool hasValue() const { return this->contains_ == Contains::VALUE; } 271 /* 272 * @returns True if the Try contains an exception, false otherwise 273 */ hasException()274 bool hasException() const { return this->contains_ == Contains::EXCEPTION; } 275 276 /* 277 * @returns True if the Try contains an exception of type Ex, false otherwise 278 */ 279 template <class Ex> hasException()280 bool hasException() const { 281 return hasException() && this->e_.template is_compatible_with<Ex>(); 282 } 283 exception()284 exception_wrapper& exception() & { 285 if (!hasException()) { 286 throw_exception<TryException>("Try does not contain an exception"); 287 } 288 return this->e_; 289 } 290 exception()291 exception_wrapper&& exception() && { 292 if (!hasException()) { 293 throw_exception<TryException>("Try does not contain an exception"); 294 } 295 return std::move(this->e_); 296 } 297 exception()298 const exception_wrapper& exception() const& { 299 if (!hasException()) { 300 throw_exception<TryException>("Try does not contain an exception"); 301 } 302 return this->e_; 303 } 304 exception()305 const exception_wrapper&& exception() const&& { 306 if (!hasException()) { 307 throw_exception<TryException>("Try does not contain an exception"); 308 } 309 return std::move(this->e_); 310 } 311 312 /* 313 * @returns a pointer to the `std::exception` held by `*this`, if one is held; 314 * otherwise, returns `nullptr`. 315 */ tryGetExceptionObject()316 std::exception* tryGetExceptionObject() { 317 return hasException() ? this->e_.get_exception() : nullptr; 318 } tryGetExceptionObject()319 std::exception const* tryGetExceptionObject() const { 320 return hasException() ? this->e_.get_exception() : nullptr; 321 } 322 323 /* 324 * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose 325 * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise, 326 * returns `nullptr`. 327 */ 328 template <class Ex> tryGetExceptionObject()329 Ex* tryGetExceptionObject() { 330 return hasException() ? this->e_.template get_exception<Ex>() : nullptr; 331 } 332 template <class Ex> tryGetExceptionObject()333 Ex const* tryGetExceptionObject() const { 334 return hasException() ? this->e_.template get_exception<Ex>() : nullptr; 335 } 336 337 /* 338 * If the Try contains an exception and it is of type Ex, execute func(Ex) 339 * 340 * @param func a function that takes a single parameter of type const Ex& 341 * 342 * @returns True if the Try held an Ex and func was executed, false otherwise 343 */ 344 template <class Ex, class F> withException(F func)345 bool withException(F func) { 346 if (!hasException()) { 347 return false; 348 } 349 return this->e_.template with_exception<Ex>(std::move(func)); 350 } 351 template <class Ex, class F> withException(F func)352 bool withException(F func) const { 353 if (!hasException()) { 354 return false; 355 } 356 return this->e_.template with_exception<Ex>(std::move(func)); 357 } 358 359 /* 360 * If the Try contains an exception and it is of type compatible with Ex as 361 * deduced from the first parameter of func, execute func(Ex) 362 * 363 * @param func a function that takes a single parameter of type const Ex& 364 * 365 * @returns True if the Try held an Ex and func was executed, false otherwise 366 */ 367 template <class F> withException(F func)368 bool withException(F func) { 369 if (!hasException()) { 370 return false; 371 } 372 return this->e_.with_exception(std::move(func)); 373 } 374 template <class F> withException(F func)375 bool withException(F func) const { 376 if (!hasException()) { 377 return false; 378 } 379 return this->e_.with_exception(std::move(func)); 380 } 381 382 template <bool isTry, typename R> get()383 typename std::enable_if<isTry, R>::type get() { 384 return std::forward<R>(*this); 385 } 386 387 template <bool isTry, typename R> get()388 typename std::enable_if<!isTry, R>::type get() { 389 return std::forward<R>(value()); 390 } 391 }; 392 393 /* 394 * Specialization of Try for void value type. Encapsulates either success or an 395 * exception. 396 */ 397 template <> 398 class Try<void> { 399 public: 400 /* 401 * The value type for the Try 402 */ 403 typedef void element_type; 404 405 // Construct a Try holding a successful and void result Try()406 Try() noexcept : hasValue_(true) {} 407 408 /* 409 * Construct a Try with an exception_wrapper 410 * 411 * @param e The exception_wrapper 412 */ Try(exception_wrapper e)413 explicit Try(exception_wrapper e) noexcept 414 : hasValue_(false), e_(std::move(e)) {} 415 416 /// Implicit conversion from Try<Unit> to Try<void> 417 /* implicit */ inline Try(const Try<Unit>& t) noexcept; 418 419 // Copy assigner 420 inline Try& operator=(const Try<void>& t) noexcept; 421 422 // Copy constructor Try(const Try<void> & t)423 Try(const Try<void>& t) noexcept : hasValue_(t.hasValue_) { 424 if (t.hasException()) { 425 new (&e_) exception_wrapper(t.e_); 426 } 427 } 428 ~Try()429 ~Try() { 430 if (hasException()) { 431 e_.~exception_wrapper(); 432 } 433 } 434 435 /* 436 * In-place construct a 'void' value into this Try object. 437 * 438 * This has the effect of clearing any existing exception stored in the 439 * Try object. 440 */ emplace()441 void emplace() noexcept { 442 if (hasException()) { 443 e_.~exception_wrapper(); 444 hasValue_ = true; 445 } 446 } 447 448 /* 449 * In-place construct an exception in the Try object. 450 * 451 * Destroys any previous value prior to constructing the new value. 452 * Leaves *this in an empty state if the construction of the exception_wrapper 453 * throws. 454 * 455 * Any arguments passed to emplaceException() are forwarded on to the 456 * exception_wrapper constructor. 457 * 458 * @returns reference to the newly constructed exception_wrapper. 459 */ 460 template <typename... Args> 461 exception_wrapper& emplaceException(Args&&... args) noexcept( 462 std::is_nothrow_constructible<exception_wrapper, Args&&...>::value); 463 464 // If the Try contains an exception, throws it value()465 void value() const { throwIfFailed(); } 466 // Dereference operator. If the Try contains an exception, throws it 467 void operator*() const { return value(); } 468 469 // If the Try contains an exception, throws it 470 inline void throwIfFailed() const; 471 inline void throwUnlessValue() const; 472 473 // @returns False if the Try contains an exception, true otherwise hasValue()474 bool hasValue() const { return hasValue_; } 475 // @returns True if the Try contains an exception, false otherwise hasException()476 bool hasException() const { return !hasValue_; } 477 478 // @returns True if the Try contains an exception of type Ex, false otherwise 479 template <class Ex> hasException()480 bool hasException() const { 481 return hasException() && e_.is_compatible_with<Ex>(); 482 } 483 484 /* 485 * @throws TryException if the Try doesn't contain an exception 486 * 487 * @returns mutable reference to the exception contained by this Try 488 */ exception()489 exception_wrapper& exception() & { 490 if (!hasException()) { 491 throw_exception<TryException>("Try does not contain an exception"); 492 } 493 return e_; 494 } 495 exception()496 exception_wrapper&& exception() && { 497 if (!hasException()) { 498 throw_exception<TryException>("Try does not contain an exception"); 499 } 500 return std::move(e_); 501 } 502 exception()503 const exception_wrapper& exception() const& { 504 if (!hasException()) { 505 throw_exception<TryException>("Try does not contain an exception"); 506 } 507 return e_; 508 } 509 exception()510 const exception_wrapper&& exception() const&& { 511 if (!hasException()) { 512 throw_exception<TryException>("Try does not contain an exception"); 513 } 514 return std::move(e_); 515 } 516 517 /* 518 * @returns a pointer to the `std::exception` held by `*this`, if one is held; 519 * otherwise, returns `nullptr`. 520 */ tryGetExceptionObject()521 std::exception* tryGetExceptionObject() { 522 return hasException() ? e_.get_exception() : nullptr; 523 } tryGetExceptionObject()524 std::exception const* tryGetExceptionObject() const { 525 return hasException() ? e_.get_exception() : nullptr; 526 } 527 528 /* 529 * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose 530 * type `From` permits `std::is_convertible<From*, Ex*>`; otherwise, 531 * returns `nullptr`. 532 */ 533 template <class E> tryGetExceptionObject()534 E* tryGetExceptionObject() { 535 return hasException() ? e_.get_exception<E>() : nullptr; 536 } 537 template <class E> tryGetExceptionObject()538 E const* tryGetExceptionObject() const { 539 return hasException() ? e_.get_exception<E>() : nullptr; 540 } 541 542 /* 543 * If the Try contains an exception and it is of type Ex, execute func(Ex) 544 * 545 * @param func a function that takes a single parameter of type const Ex& 546 * 547 * @returns True if the Try held an Ex and func was executed, false otherwise 548 */ 549 template <class Ex, class F> withException(F func)550 bool withException(F func) { 551 if (!hasException()) { 552 return false; 553 } 554 return e_.with_exception<Ex>(std::move(func)); 555 } 556 template <class Ex, class F> withException(F func)557 bool withException(F func) const { 558 if (!hasException()) { 559 return false; 560 } 561 return e_.with_exception<Ex>(std::move(func)); 562 } 563 564 /* 565 * If the Try contains an exception and it is of type compatible with Ex as 566 * deduced from the first parameter of func, execute func(Ex) 567 * 568 * @param func a function that takes a single parameter of type const Ex& 569 * 570 * @returns True if the Try held an Ex and func was executed, false otherwise 571 */ 572 template <class F> withException(F func)573 bool withException(F func) { 574 if (!hasException()) { 575 return false; 576 } 577 return e_.with_exception(std::move(func)); 578 } 579 template <class F> withException(F func)580 bool withException(F func) const { 581 if (!hasException()) { 582 return false; 583 } 584 return e_.with_exception(std::move(func)); 585 } 586 587 template <bool, typename R> get()588 R get() { 589 return std::forward<R>(*this); 590 } 591 592 private: 593 bool hasValue_; 594 union { 595 exception_wrapper e_; 596 }; 597 }; 598 599 template <typename T> 600 struct isTry : std::false_type {}; 601 602 template <typename T> 603 struct isTry<Try<T>> : std::true_type {}; 604 605 /* 606 * @param f a function to execute and capture the result of (value or exception) 607 * 608 * @returns Try holding the result of f 609 */ 610 template <typename F> 611 typename std::enable_if< 612 !std::is_same<invoke_result_t<F>, void>::value, 613 Try<invoke_result_t<F>>>::type 614 makeTryWithNoUnwrap(F&& f); 615 616 /* 617 * Specialization of makeTryWith for void return 618 * 619 * @param f a function to execute and capture the result of 620 * 621 * @returns Try<void> holding the result of f 622 */ 623 template <typename F> 624 typename std:: 625 enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type 626 makeTryWithNoUnwrap(F&& f); 627 628 /* 629 * @param f a function to execute and capture the result of (value or exception) 630 * 631 * @returns Try holding the result of f 632 */ 633 template <typename F> 634 typename std:: 635 enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type 636 makeTryWith(F&& f); 637 638 /* 639 * Specialization of makeTryWith for functions that return Try<T> 640 * Causes makeTryWith to not double-wrap the try. 641 * 642 * @param f a function to execute and capture the result of 643 * 644 * @returns result of f if f did not throw. Otherwise Try<T> containing 645 * exception 646 */ 647 template <typename F> 648 typename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>:: 649 type 650 makeTryWith(F&& f); 651 652 /* 653 * Try to in-place construct a new value from the specified arguments. 654 * 655 * If T's constructor throws an exception then this is caught and the Try<T> 656 * object is initialised to hold that exception. 657 * 658 * @param args Are passed to T's constructor. 659 */ 660 template <typename T, typename... Args> 661 T* tryEmplace(Try<T>& t, Args&&... args) noexcept; 662 663 /* 664 * Overload of tryEmplace() for Try<void>. 665 */ 666 inline void tryEmplace(Try<void>& t) noexcept; 667 668 /* 669 * Try to in-place construct a new value from the result of a function. 670 * 671 * If the function completes successfully then attempts to in-place construct 672 * a value of type, T, passing the result of the function as the only parameter. 673 * 674 * If either the call to the function completes with an exception or the 675 * constructor completes with an exception then the exception is caught and 676 * stored in the Try object. 677 * 678 * @returns A pointer to the newly constructed object if it completed 679 * successfully, otherwise returns nullptr if the operation completed with 680 * an exception. 681 */ 682 template <typename T, typename Func> 683 T* tryEmplaceWith(Try<T>& t, Func&& func) noexcept; 684 685 /* 686 * Specialization of tryEmplaceWith() for Try<void>. 687 * 688 * Calls func() and if it doesn't throw an exception then calls t.emplace(). 689 * If func() throws then captures the exception in t using t.emplaceException(). 690 * 691 * Func must be callable with zero parameters and must return void. 692 * 693 * @returns true if func() completed without an exception, false if func() 694 * threw an exception. 695 */ 696 template <typename Func> 697 bool tryEmplaceWith(Try<void>& t, Func&& func) noexcept; 698 699 /** 700 * Tuple<Try<Type>...> -> std::tuple<Type...> 701 * 702 * Unwraps a tuple-like type containing a sequence of Try<Type> instances to 703 * std::tuple<Type> 704 */ 705 template <typename Tuple> 706 auto unwrapTryTuple(Tuple&&); 707 708 /* 709 * Try to move the value/exception from another Try object. 710 * 711 * If T's constructor throws an exception then this is caught and the Try<T> 712 * object is initialised to hold that exception. 713 */ 714 template <typename T> 715 void tryAssign(Try<T>& t, Try<T>&& other) noexcept; 716 717 #if FOLLY_CPLUSPLUS >= 201703L 718 template <typename T> 719 Try(T&&) -> Try<std::decay_t<T>>; 720 #endif 721 722 } // namespace folly 723 724 #include <folly/Try-inl.h> 725