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: |
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 | */ |
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 | */ |
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 | */ |
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> |
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 | */ |
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 | */ |
242 | bool hasValue() const { |
243 | return contains_ == Contains::VALUE; |
244 | } |
245 | /* |
246 | * @returns True if the Try contains an exception, false otherwise |
247 | */ |
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> |
256 | bool hasException() const { |
257 | return hasException() && e_.is_compatible_with<Ex>(); |
258 | } |
259 | |
260 | exception_wrapper& exception() & { |
261 | if (!hasException()) { |
262 | throw_exception<TryException>("Try does not contain an exception" ); |
263 | } |
264 | return e_; |
265 | } |
266 | |
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 | |
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 | |
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 | */ |
292 | std::exception* tryGetExceptionObject() { |
293 | return hasException() ? e_.get_exception() : nullptr; |
294 | } |
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> |
305 | E* tryGetExceptionObject() { |
306 | return hasException() ? e_.get_exception<E>() : nullptr; |
307 | } |
308 | template <class E> |
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> |
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> |
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> |
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> |
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> |
359 | typename std::enable_if<isTry, R>::type get() { |
360 | return std::forward<R>(*this); |
361 | } |
362 | |
363 | template <bool isTry, typename R> |
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 |
391 | Try() noexcept : hasValue_(true) {} |
392 | |
393 | /* |
394 | * Construct a Try with an exception_wrapper |
395 | * |
396 | * @param e The exception_wrapper |
397 | */ |
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 |
405 | Try(const Try<void>& t) noexcept : hasValue_(t.hasValue_) { |
406 | if (t.hasException()) { |
407 | new (&e_) exception_wrapper(t.e_); |
408 | } |
409 | } |
410 | |
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 | */ |
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 |
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 |
459 | bool hasValue() const { |
460 | return hasValue_; |
461 | } |
462 | // @returns True if the Try contains an exception, false otherwise |
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> |
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 | */ |
478 | exception_wrapper& exception() & { |
479 | if (!hasException()) { |
480 | throw_exception<TryException>("Try does not contain an exception" ); |
481 | } |
482 | return e_; |
483 | } |
484 | |
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 | |
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 | |
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 | */ |
510 | std::exception* tryGetExceptionObject() { |
511 | return hasException() ? e_.get_exception() : nullptr; |
512 | } |
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> |
523 | E* tryGetExceptionObject() { |
524 | return hasException() ? e_.get_exception<E>() : nullptr; |
525 | } |
526 | template <class E> |
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> |
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> |
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> |
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> |
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> |
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 | |