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