unwrapTryTuple only accepted rvalue tuple types
[folly.git] / folly / Try-inl.h
1 /*
2  * Copyright 2017 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
17 #pragma once
18
19 #include <folly/Utility.h>
20
21 #include <stdexcept>
22 #include <tuple>
23
24 namespace folly {
25
26 template <class T>
27 Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) {
28   if (contains_ == Contains::VALUE) {
29     new (&value_)T(std::move(t.value_));
30   } else if (contains_ == Contains::EXCEPTION) {
31     new (&e_) exception_wrapper(std::move(t.e_));
32   }
33 }
34
35 template <class T>
36 template <class T2>
37 Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value,
38                                     Try<void> const&>::type t)
39     : contains_(Contains::NOTHING) {
40   if (t.hasValue()) {
41     contains_ = Contains::VALUE;
42     new (&value_) T();
43   } else if (t.hasException()) {
44     contains_ = Contains::EXCEPTION;
45     new (&e_) exception_wrapper(t.exception());
46   }
47 }
48
49 template <class T>
50 Try<T>& Try<T>::operator=(Try<T>&& t) noexcept {
51   if (this == &t) {
52     return *this;
53   }
54
55   this->~Try();
56   contains_ = t.contains_;
57   if (contains_ == Contains::VALUE) {
58     new (&value_)T(std::move(t.value_));
59   } else if (contains_ == Contains::EXCEPTION) {
60     new (&e_) exception_wrapper(std::move(t.e_));
61   }
62   return *this;
63 }
64
65 template <class T>
66 Try<T>::Try(const Try<T>& t) {
67   static_assert(
68       std::is_copy_constructible<T>::value,
69       "T must be copyable for Try<T> to be copyable");
70   contains_ = t.contains_;
71   if (contains_ == Contains::VALUE) {
72     new (&value_)T(t.value_);
73   } else if (contains_ == Contains::EXCEPTION) {
74     new (&e_) exception_wrapper(t.e_);
75   }
76 }
77
78 template <class T>
79 Try<T>& Try<T>::operator=(const Try<T>& t) {
80   static_assert(
81       std::is_copy_constructible<T>::value,
82       "T must be copyable for Try<T> to be copyable");
83   this->~Try();
84   contains_ = t.contains_;
85   if (contains_ == Contains::VALUE) {
86     new (&value_)T(t.value_);
87   } else if (contains_ == Contains::EXCEPTION) {
88     new (&e_) exception_wrapper(t.e_);
89   }
90   return *this;
91 }
92
93 template <class T>
94 Try<T>::~Try() {
95   if (LIKELY(contains_ == Contains::VALUE)) {
96     value_.~T();
97   } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) {
98     e_.~exception_wrapper();
99   }
100 }
101
102 template <class T>
103 T& Try<T>::value() & {
104   throwIfFailed();
105   return value_;
106 }
107
108 template <class T>
109 T&& Try<T>::value() && {
110   throwIfFailed();
111   return std::move(value_);
112 }
113
114 template <class T>
115 const T& Try<T>::value() const & {
116   throwIfFailed();
117   return value_;
118 }
119
120 template <class T>
121 void Try<T>::throwIfFailed() const {
122   switch (contains_) {
123     case Contains::VALUE:
124       return;
125     case Contains::EXCEPTION:
126       e_.throw_exception();
127     default:
128       try_detail::throwUsingUninitializedTry();
129   }
130 }
131
132 void Try<void>::throwIfFailed() const {
133   if (!hasValue_) {
134     e_.throw_exception();
135   }
136 }
137
138 template <typename F>
139 typename std::enable_if<
140   !std::is_same<typename std::result_of<F()>::type, void>::value,
141   Try<typename std::result_of<F()>::type>>::type
142 makeTryWith(F&& f) {
143   typedef typename std::result_of<F()>::type ResultType;
144   try {
145     return Try<ResultType>(f());
146   } catch (std::exception& e) {
147     return Try<ResultType>(exception_wrapper(std::current_exception(), e));
148   } catch (...) {
149     return Try<ResultType>(exception_wrapper(std::current_exception()));
150   }
151 }
152
153 template <typename F>
154 typename std::enable_if<
155   std::is_same<typename std::result_of<F()>::type, void>::value,
156   Try<void>>::type
157 makeTryWith(F&& f) {
158   try {
159     f();
160     return Try<void>();
161   } catch (std::exception& e) {
162     return Try<void>(exception_wrapper(std::current_exception(), e));
163   } catch (...) {
164     return Try<void>(exception_wrapper(std::current_exception()));
165   }
166 }
167
168 namespace try_detail {
169
170 /**
171  * Trait that removes the layer of Try abstractions from the passed in type
172  */
173 template <typename Type>
174 struct RemoveTry;
175 template <template <typename...> class TupleType, typename... Types>
176 struct RemoveTry<TupleType<folly::Try<Types>...>> {
177   using type = TupleType<Types...>;
178 };
179
180 template <std::size_t... Indices, typename Tuple>
181 auto unwrapTryTupleImpl(folly::index_sequence<Indices...>, Tuple&& instance) {
182   using std::get;
183   using ReturnType = typename RemoveTry<typename std::decay<Tuple>::type>::type;
184   return ReturnType{(get<Indices>(std::forward<Tuple>(instance)).value())...};
185 }
186 } // namespace try_detail
187
188 template <typename Tuple>
189 auto unwrapTryTuple(Tuple&& instance) {
190   using TupleDecayed = typename std::decay<Tuple>::type;
191   using Seq = folly::make_index_sequence<std::tuple_size<TupleDecayed>::value>;
192   return try_detail::unwrapTryTupleImpl(Seq{}, std::forward<Tuple>(instance));
193 }
194
195 } // namespace folly