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