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