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
32namespace folly {
33
34class FOLLY_EXPORT TryException : public std::logic_error {
35 public:
36 using std::logic_error::logic_error;
37};
38
39class 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 */
51template <class T>
52class 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 */
383template <>
384class 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
589template <typename T>
590struct isTry : std::false_type {};
591
592template <typename T>
593struct 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 */
600template <typename F>
601typename std::enable_if<
602 !std::is_same<invoke_result_t<F>, void>::value,
603 Try<invoke_result_t<F>>>::type
604makeTryWithNoUnwrap(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 */
613template <typename F>
614typename 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 */
623template <typename F>
624typename 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 */
637template <typename F>
638typename 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 */
650template <typename T, typename... Args>
651T* tryEmplace(Try<T>& t, Args&&... args) noexcept;
652
653/*
654 * Overload of tryEmplace() for Try<void>.
655 */
656inline 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 */
672template <typename T, typename Func>
673T* 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 */
686template <typename Func>
687bool 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 */
695template <typename Tuple>
696auto unwrapTryTuple(Tuple&&);
697
698} // namespace folly
699
700#include <folly/Try-inl.h>
701