• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef IORAP_SRC_COMMON_EXPECTED_H_
16 #define IORAP_SRC_COMMON_EXPECTED_H_
17 
18 #include <type_traits>
19 #include <utility>
20 
21 #include <android-base/logging.h>  // CHECK/DCHECK.
22 
23 // Ignore the tautological-undefined-compare warning.
24 // We obviously want to do this to protect against undefined behavior
25 // that sets a reference to a null value.
26 #define DCHECK_UB_NOT_NULL(x) \
27   DCHECK(reinterpret_cast<volatile decltype(x)>(x) != nullptr)
28 
29 /**
30  * Result<Value, Error>-like interface.
31  *
32  * Subset of the experimental standard C++ proposal (p0323r3)
33  *
34  * Example:
35  *
36  *   expected<std::string, status_t> x = function_which_might_fail();
37  *   if (x) {
38  *     std::string str = x.value();
39  *   } else {
40  *     status_t err = x.error();
41  *   }
42  */
43 
44 namespace iorap {
45 namespace detail {
46   // Use perfect forwarding for expected_data constructors with overloading.
47   struct expected_tag{};
48   struct expected_tag_right : public expected_tag {
49     static constexpr bool is_right_v = true;
50   };
51   struct expected_tag_error : public expected_tag {
52     static constexpr bool is_right_v = false;
53   };
54 
55   template <typename T, typename E, bool DefineDestructor>
56   struct expected_data;
57 
58   // This doesn't always work because this code could be instantiated with a non-trivial T/E,
59   // and then the union becomes invalid.
60   template <typename T, typename E>
61   struct expected_data<T, E, /*DefineDestructor*/true> {
62     // Mark everything 'constexpr' to keep the code the same as the other partial specialization.
63 
64     template <typename U>
65     constexpr expected_data(U&& either, expected_tag_right)
66         : right_{std::forward<U>(either)}, is_right_{true} {}
67 
68     template <typename U>
69     constexpr expected_data(U&& either, expected_tag_error)
70         : error_{std::forward<U>(either)}, is_right_{false} {}
71 
72     constexpr bool has_value() const {
73       return is_right_;
74     }
75 
76     constexpr const T& value() const {
77       return right_;
78     }
79 
80     constexpr T& value() {
81       return right_;
82     }
83 
84     constexpr const E& error() const {
85       return error_;
86     }
87 
88     constexpr E& error() {
89       return error_;
90     }
91 
92     // Using an "anonymous union" here allows non-trivial types to be stored.
93     union {
94       T right_;
95       E error_;
96     };
97 
98     bool is_right_;
99 
100     // Below code differs slightly by handling non-trivial constructors/destructors.
101     bool moved_from_{false};
102 
103     // Note: Destructors cannot be templated, so it is illegal to use SFINAE to try to
104     // conditionalize this destructor somehow.
105     ~expected_data() {
106       if (moved_from_) { return; }
107       if (is_right_) {
108         right_.~T();
109       } else {
110         error_.~E();
111       }
112     }
113 
114     expected_data(expected_data&& other)
115         noexcept(
116             noexcept(T(std::move(other.right_))) &&
117             noexcept(E(std::move(other.error_)))
118         ) {
119       DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
120       DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
121       if (other.is_right_) {
122         new (&right_) T(std::move(other.right_));
123       } else {
124         new (&error_) E(std::move(other.error_));
125       }
126       other.moved_from_ = true;
127       is_right_ = other.is_right_;
128     }
129 
130     expected_data(const expected_data& other) {
131       DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
132       DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
133       if (other.is_right_) {
134         new (&right_) T(other.right_);
135       } else {
136         new (&error_) E(other.error_);
137       }
138       is_right_ = other.is_right_;
139     }
140 
141     expected_data& operator=(const expected_data& other) {
142       DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
143       DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
144 
145       if (this == &other) {
146         return *this;
147       }
148 
149       if (other.is_right_) {
150         if (!is_right_) {
151           error_.~E();
152           new (&right_) T(other.right_);
153         } else {
154           right_ = other.right_;
155         }
156       } else {
157         if (is_right_) {
158           right_.~T();
159           new (&error_) E(other.error_);
160         } else {
161           error_ = other.error_;
162         }
163       }
164       is_right_ = other.is_right_;
165 
166       return *this;
167     }
168 
169     expected_data& operator=(expected_data&& other) {
170       DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
171       DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
172 
173       if (this == &other) {
174         return *this;
175       }
176 
177       if (other.is_right_) {
178         if (!is_right_) {
179           error_.~E();
180           new (&right_) T(std::move(other.right_));
181         } else {
182           right_ = std::move(other.right_);
183         }
184       } else {
185         if (is_right_) {
186           right_.~T();
187           new (&error_) E(std::move(other.error_));
188         } else {
189           error_ = std::move(other.error_);
190         }
191       }
192 
193       other.moved_from_ = true;
194       is_right_ = other.is_right_;
195 
196       return *this;
197     }
198   };
199 
200   // Trivial-destructor copy of the above struct.
201   //
202   // A separate copy is required because otherwise compilation fails with an error about
203   // the union having an implicitly deleted constructor.
204   //
205   // Having this implementation gives us the property that
206   //
207   //     (is_trivially_destructible<T> && is_trivially_destructible<E>
208   //       ==> is_trivially_destructible<expected<T, E>>)
209   template <typename T, typename E>
210   struct expected_data<T, E, /*DefineDestructor*/false> {
211     template <typename U>
212     constexpr expected_data(U&& either, expected_tag_right)
213         : right_{std::forward<U>(either)}, is_right_{true} {}
214 
215     template <typename U>
216     constexpr expected_data(U&& either, expected_tag_error)
217         : error_{std::forward<U>(either)}, is_right_{false} {}
218 
219     constexpr bool has_value() const {
220       return is_right_;
221     }
222 
223     constexpr const T& value() const {
224       return right_;
225     }
226 
227     constexpr T& value() {
228       return right_;
229     }
230 
231     constexpr const E& error() const {
232       return error_;
233     }
234 
235     constexpr E& error() {
236       return error_;
237     }
238 
239     // Using an "anonymous union" here allows non-trivial types to be stored.
240     union {
241       T right_;
242       E error_;
243     };
244 
245     bool is_right_;
246 
247     ~expected_data() = default;
248   };
249 
250   // Select between trivial and non-trivial implementations. Trivial implementations
251   // are more optimized and constexpr-compatible.
252   template <typename T, typename E>
253   using expected_pick_data_t =
254       expected_data<T, E,
255         !(std::is_trivially_destructible_v<T> && std::is_trivially_destructible_v<E>) >;
256 }  // namespace detail
257 
258 template <typename E>
259 struct unexpected;
260 
261 // Subset of std::experimental::expected proposal (p0323r3).
262 template <typename T, typename E>
263 struct expected {
264   // Never-empty: expected<T,E> values have either 'T' or 'E' in them.
265   template <typename U = T, typename _ = std::enable_if_t<std::is_default_constructible_v<U>>>
266   constexpr expected() noexcept(noexcept(T{})) : expected(T{}) {}
267 
268   constexpr expected(const T& value) : data_{value, detail::expected_tag_right{}} {}
269   constexpr expected(T&& value) : data_{std::move(value), detail::expected_tag_right{}} {}
270   constexpr expected(const E& error) : data_{error, detail::expected_tag_error{}} {}
271   constexpr expected(E&& error) : data_{std::move(error), detail::expected_tag_error{}} {}
272 
273   template <typename G = E>
274   constexpr expected(unexpected<G> const& u) : expected{u.value()} {}
275 
276   template <typename G = E>
277   constexpr expected(unexpected<G>&& u) : expected{std::move(u.value())} {}
278 
279   explicit constexpr operator bool() const {
280     return has_value();
281   }
282 
283   constexpr bool has_value() const {
284     return data_.has_value();
285   }
286 
287   constexpr const T& operator*() const {
288     return data_.value();
289   }
290 
291   constexpr T& operator*() {
292     return data_.value();
293   }
294 
295   // TODO: arrow operator?
296 
297   constexpr T& value() & {
298     CHECK(has_value());
299     return data_.value();
300   }
301 
302   constexpr const T& value() const & {
303     CHECK(has_value());
304     return data_.value();
305   }
306 
307   constexpr T&& value() && {
308     CHECK(has_value());
309     return std::move(data_.value());
310   }
311 
312   constexpr const T& value() const && {
313     CHECK(has_value());
314     return std::move(data_.value());
315   }
316 
317   constexpr E& error() {
318     DCHECK(!has_value());
319     return data_.error();
320   }
321 
322   constexpr const E& error() const {
323     DCHECK(!has_value());
324     return data_.error();
325   }
326 
327   // TODO: other functions such as operator=, unexpected, etc.
328  private:
329   detail::expected_pick_data_t<T, E> data_;
330 };
331 
332 // TODO: move to tests file
333 namespace {
334   struct TestType {
335     TestType() {}
336     ~TestType() {}
337   };
338   struct TestType2 : TestType {};
339 
340   static_assert(std::is_trivially_destructible_v<expected<int, /*error*/double> >);
341   static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/double> >);
342   static_assert(!std::is_trivially_destructible_v<expected<int, /*error*/TestType> >);
343   static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/TestType2> >);
344 
345   // Ensure expected is constexpr-compatible.
346   struct TestCase {
347     static constexpr auto t1 = expected<int, double>{};
348   };
349 }  // namespace <anonymous>
350 
351 template <typename E>
352 struct unexpected {
353   unexpected() = delete;
354   constexpr explicit unexpected(const E& error) : error_{error} {}
355   constexpr explicit unexpected(E&& error) : error_{std::move(error)} {}
356   constexpr const E& value() const& { return error_; }
357   constexpr E& value() & { return error_; }
358   constexpr E&& value() && { return std::move(error_); }
359   constexpr E const&& value() const&& { return std::move(error_); }
360  private:
361   E error_;
362 };
363 
364 template <class E>
365 constexpr bool operator==(const unexpected<E>& x, const unexpected<E>& y) {
366   return x.value() == y.value();
367 }
368 
369 template <class E>
370 constexpr bool operator!=(const unexpected<E>& x, const unexpected<E>& y) {
371   return !(x == y);
372 }
373 
374 // TODO: move below codes to separate utils file
375 //
376 // future C++20 implementation of std::identity
377 struct identity {
378   template <typename U>
379   constexpr auto operator()(U&& v) const noexcept {
380     return std::forward<U>(v);
381   }
382 };
383 
384 // Given a lambda [...](auto&& var) {...}
385 //   apply std::forward to 'var' to achieve perfect forwarding.
386 //
387 // Note that this doesn't work when var is a template type, i.e.
388 //   template <typename T>
389 //   void func(T&& tvar) {...}
390 //
391 // It would be invalid to use this macro with 'tvar' in that context.
392 #define IORAP_FORWARD_LAMBDA(var) std::forward<decltype(var)>(var)
393 
394 // Borrowed non-null pointer, i.e. we do not own the lifetime.
395 //
396 // Function calls: This pointer is not used past the call.
397 // Struct fields: This pointer is not used past the lifetime of the struct.
398 template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
399 using borrowed = T _Nonnull;
400 // TODO: need a DCHECK or high warning levels, since null is technically well-defined.
401 
402 }  // namespace iorap
403 
404 #endif  // IORAP_SRC_COMMON_EXPECTED_H_
405