Try.h 18 KB

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