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
31namespace folly {
32
33class FOLLY_EXPORT TryException : public std::logic_error {
34 public:
35 using std::logic_error::logic_error;
36};
37
38class 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 */
50template <class T>
51class 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 */
382template <>
383class 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 */
593template <typename F>
594typename std::enable_if<
595 !std::is_same<invoke_result_t<F>, void>::value,
596 Try<invoke_result_t<F>>>::type
597makeTryWith(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 */
606template <typename F>
607typename 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 */
619template <typename T, typename... Args>
620T* tryEmplace(Try<T>& t, Args&&... args) noexcept;
621
622/*
623 * Overload of tryEmplace() for Try<void>.
624 */
625inline 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 */
641template <typename T, typename Func>
642T* 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 */
655template <typename Func>
656bool 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 */
664template <typename Tuple>
665auto unwrapTryTuple(Tuple&&);
666
667} // namespace folly
668
669#include <folly/Try-inl.h>
670