1 /* 2 * Copyright 2019 Google LLC 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 #ifndef FCP_BASE_RESULT_H_ 18 #define FCP_BASE_RESULT_H_ 19 20 #include <optional> 21 #include <type_traits> 22 #include <variant> 23 24 #include "fcp/base/error.h" 25 #include "fcp/base/meta.h" 26 #include "fcp/base/source_location.h" 27 28 namespace fcp { 29 namespace result_internal { 30 31 template <typename R> 32 struct ResultTraits; 33 34 } // namespace result_internal 35 36 // A Result is either a value (T) or an opaque Error. There are two main ways to 37 // use one. 38 // 39 // Transform: Given a Result<T> r, and a callable f from T to Result<U>, 40 // r.Then(f) returns Result<U>. Note that errors are passed through (without 41 // calling f). 42 // 43 // Similarly, given a Result<T> r, and a callable f from T to U, r.Map(f) 44 // returns Result<U>. The difference is that Then can introduce new errors (it 45 // returns Result<U>) whereas Map only transforms values to other values. 46 // 47 // Result<int> some_int = ... 48 // Result<bool> b = some_int.Then([](int i) -> Result<int> { 49 // if (i < 0) { 50 // return TraceError<...>(...); 51 // } else { 52 // return i; 53 // } 54 // }).Map([](int i) -> bool) { 55 // return i % 2 == 0; 56 // }); 57 // 58 // Propagate: The FCP_TRY macro unwraps results to their values. If a result 59 // contains an error, it is returned (from the function where FCP_TRY appears). 60 // 61 // Result<int> GetInt(); 62 // 63 // Result<bool> F() { 64 // int i = FCP_TRY(GetInt()); 65 // if (i < 0) { 66 // } 67 // 68 // return i % 2 == 0; 69 // } 70 // 71 // Result<T> provides implicit conversions from T and Error. As above, in 72 // functions returning Result<T>, it is useful and encourage to return a T or 73 // Error directly. 74 template <typename T> 75 class ABSL_MUST_USE_RESULT Result { 76 public: 77 using ValueType = T; 78 79 // These make Result<> usable as an argument to Match() (see match.h). 80 using VariantType = std::variant<Error, T>; variant()81 constexpr VariantType& variant() & { return val_; } variant()82 constexpr VariantType const& variant() const& { return val_; } variant()83 constexpr VariantType&& variant() && { return std::move(val_); } 84 85 // Implicit conversion from T Result(T t)86 constexpr Result(T t) : val_(std::move(t)) {} // NOLINT 87 88 // Implicit conversion from Error Result(Error e)89 Result(Error e) : val_(e) {} // NOLINT 90 is_error()91 constexpr bool is_error() const { 92 return std::holds_alternative<Error>(val_); 93 } 94 95 // Returns a *reference* to the contained value. 96 // Requires (CHECK): !is_error() GetValueOrDie()97 constexpr T const& GetValueOrDie() const& { 98 FCP_CHECK(std::holds_alternative<T>(val_)); 99 return absl::get<T>(val_); 100 } 101 102 // Returns the contained value (by move). 103 // This applies for results which are rvalues. 104 // 105 // Example: 106 // Result<X> r = f(); 107 // X v = std::move(r).GetValueOrDie(); 108 // 109 // Example: 110 // X v = f().GetValueOrDie(); 111 // 112 // Requires (CHECK): !is_error() GetValueOrDie()113 constexpr T GetValueOrDie() && { 114 FCP_CHECK(std::holds_alternative<T>(val_)); 115 return absl::get<T>(std::move(val_)); 116 } 117 118 // Returns the contained error. 119 // Requires (CHECK): is_error() GetErrorOrDie()120 Error GetErrorOrDie() const { 121 FCP_CHECK(std::holds_alternative<Error>(val_)); 122 return absl::get<Error>(val_); 123 } 124 125 // Transforms this Result into another (with value type U). 126 // 127 // If this Result holds an Error, it is passed through. 128 // If this Result holds a value, then the callable 'fn' is applied to it. 129 // The callable 'fn' is expected to return Result<U>. 130 // 131 // Example: 132 // 133 // Result<int> some_int = ... 134 // Result<bool> b = some_int.Then([](int i) -> Result<bool> { 135 // if (i < 0) { 136 // return TraceError<...>(...); 137 // } else { 138 // return i % 2 == 0; 139 // } 140 // }); 141 template <typename Fn> Then(Fn fn)142 constexpr auto Then(Fn fn) const& { 143 return ThenInternal<false>(*this, std::move(fn)); 144 } 145 146 template <typename Fn> Then(Fn fn)147 constexpr auto Then(Fn fn) && { 148 return ThenInternal<true>(std::move(*this), std::move(fn)); 149 } 150 151 // Maps values of type T to a values of type U. 152 // 153 // If this Result holds an Error, it is passed through. 154 // If this Result holds a value, then the callable 'fn' is applied to it. 155 // 156 // Example: 157 // 158 // Result<int> some_int = ... 159 // Result<bool> b = some_int.Map([](int i) { 160 // return i % 2 == 0; 161 // }); 162 template <typename Fn> Map(Fn fn)163 constexpr auto Map(Fn fn) const& { 164 using U = std::invoke_result_t<Fn, T const&>; 165 return ThenInternal<false>( 166 *this, [fn = std::move(fn)](T const& t) { return Result<U>(fn(t)); }); 167 } 168 169 template <typename Fn> Map(Fn fn)170 constexpr auto Map(Fn fn) && { 171 using U = std::invoke_result_t<Fn, T&&>; 172 return ThenInternal<true>(std::move(*this), [fn = std::move(fn)](T&& t) { 173 return Result<U>(fn(std::move(t))); 174 }); 175 } 176 177 private: 178 template <bool Move, typename Fn> ThenInternal(std::conditional_t<Move,Result<T> &&,Result<T> const &> r,Fn fn)179 static constexpr auto ThenInternal( 180 std::conditional_t<Move, Result<T>&&, Result<T> const&> r, Fn fn) { 181 using RefType = std::conditional_t<Move, T&&, T const&>; 182 using RetType = std::invoke_result_t<Fn, RefType>; 183 static_assert( 184 result_internal::ResultTraits<RetType>::is_result(), 185 "The callable provided to 'Then' must return Result<U> for " 186 "some type U. When always returning a value, use Map instead."); 187 188 if (r.is_error()) { 189 return RetType(r.GetErrorOrDie()); 190 } else { 191 return fn(absl::get<T>(std::move(r).variant())); 192 } 193 } 194 195 std::variant<Error, T> val_; 196 }; 197 198 // This is a deduction guide so that one can write Result(t) for a value t, 199 // without explicitly specifying the value type. This one is implicitly 200 // declared anyway; we make it explicit to suppress -Wctad-maybe-unsupported. 201 template <typename T> 202 Result(T) -> Result<T>; 203 204 // ResultFrom<T> -> Result<T> 205 // ResultFrom<Result<T>> -> Result<T> 206 // 207 // Note that ResultFrom<Error> is ill-formed (like Result<Error>). 208 template <typename T> 209 using ResultFrom = decltype(Result(std::declval<T>())); 210 211 // ResultOf applied to the result of calling Fn with Args... 212 template <typename Fn, typename... Args> 213 using ResultOf = ResultFrom<std::invoke_result_t<Fn, Args...>>; 214 215 namespace result_internal { 216 217 template <typename R> 218 struct ResultTraits { 219 using ValueType = void; 220 }; 221 222 template <typename T> 223 struct ResultTraits<Result<T>> { 224 static constexpr bool is_result() { return true; } 225 using ValueType = T; 226 }; 227 228 // This is used in FCP_TRY, to require that the parameter to FCP_TRY has type 229 // Result<T> for some T. 230 template <typename T> 231 constexpr bool ResultIsError(Result<T> const& r) { 232 return r.is_error(); 233 } 234 235 } // namespace result_internal 236 237 class ExpectBase { 238 public: 239 constexpr explicit ExpectBase(SourceLocation loc) : loc_(loc) {} 240 241 protected: 242 Error TraceExpectError(const char* expectation) const; 243 Error TraceUnexpectedStatus(fcp::StatusCode expected_code, 244 const fcp::Status& actual) const; 245 246 private: 247 SourceLocation loc_; 248 }; 249 250 // Returns Result<T> if the current result has std::variant that holds a 251 // value of type T; otherwise returns an error Result. 252 template <typename T> 253 struct ExpectIs : public ExpectBase { 254 using ExpectBase::ExpectBase; 255 constexpr explicit ExpectIs(SourceLocation loc = SourceLocation::current()) 256 : ExpectBase(loc) {} 257 258 template <typename... Us> 259 constexpr Result<T> operator()(std::variant<Us...> v) const { 260 if (std::holds_alternative<T>(v)) { 261 return absl::get<T>(std::move(v)); 262 } else { 263 return TraceExpectError("ExpectIs"); 264 } 265 } 266 }; 267 268 // Returns Result<std::variant<Us...>> if the current result has 269 // std::variant that holds a value of one of the types from Us... typelist; 270 // otherwise returns an error Result. This operation is valid only when the 271 // set of expected types Us... is a subset of the set of types Ts... in the 272 // current result. 273 template <typename... Ts> 274 struct ExpectOneOf : public ExpectBase { 275 using ExpectBase::ExpectBase; 276 constexpr explicit ExpectOneOf(SourceLocation loc = SourceLocation::current()) 277 : ExpectBase(loc) {} 278 279 template <typename... Us> 280 constexpr Result<std::variant<Ts...>> operator()( 281 std::variant<Us...> v) const { 282 static_assert(IsSubsetOf<Pack<Ts...>, Pack<Us...>>::value); 283 284 // TODO(team): This should be expressible with Match 285 return absl::visit( 286 [this](auto arg) -> Result<std::variant<Ts...>> { 287 if constexpr (IsTypeOneOf<std::decay_t<decltype(arg)>, Ts...>()) { 288 return std::variant<Ts...>(std::move(arg)); 289 } else { 290 return TraceExpectError("ExpectOneOf<>"); 291 } 292 }, 293 std::move(v)); 294 } 295 }; 296 297 // Returns Result<Unit> if the current result has boolean 'true' value; 298 // otherwise returns an error Result. 299 struct ExpectTrue : public ExpectBase { 300 using ExpectBase::ExpectBase; 301 constexpr explicit ExpectTrue(SourceLocation loc = SourceLocation::current()) 302 : ExpectBase(loc) {} 303 304 template <typename... Us> 305 constexpr Result<Unit> operator()(bool b) const { 306 if (b) { 307 return Unit{}; 308 } else { 309 return TraceExpectError("ExpectTrue"); 310 } 311 } 312 }; 313 314 // Returns Result<Unit> if the current result has boolean 'false' value; 315 // otherwise returns an error Result. 316 struct ExpectFalse : public ExpectBase { 317 using ExpectBase::ExpectBase; 318 constexpr explicit ExpectFalse(SourceLocation loc = SourceLocation::current()) 319 : ExpectBase(loc) {} 320 321 template <typename... Us> 322 constexpr Result<Unit> operator()(bool b) const { 323 if (!b) { 324 return Unit{}; 325 } else { 326 return TraceExpectError("ExpectTrue"); 327 } 328 } 329 }; 330 331 // Returns Result<T> if the current result has std::optional<T> has a value; 332 // otherwise returns an error Result. 333 struct ExpectHasValue : public ExpectBase { 334 using ExpectBase::ExpectBase; 335 constexpr explicit ExpectHasValue( 336 SourceLocation loc = SourceLocation::current()) 337 : ExpectBase(loc) {} 338 339 template <typename T> 340 constexpr Result<T> operator()(std::optional<T> v) const { 341 if (v.has_value()) { 342 return *std::move(v); 343 } else { 344 return TraceExpectError("ExpectHasValue"); 345 } 346 } 347 }; 348 349 // Returns Result<Unit> if the current result has an empty std::optional; 350 // otherwise returns an error Result. 351 struct ExpectIsEmpty : public ExpectBase { 352 using ExpectBase::ExpectBase; 353 constexpr explicit ExpectIsEmpty( 354 SourceLocation loc = SourceLocation::current()) 355 : ExpectBase(loc) {} 356 357 template <typename T> 358 constexpr Result<Unit> operator()(std::optional<T> v) const { 359 if (!v.has_value()) { 360 return Unit{}; 361 } else { 362 return TraceExpectError("ExpectIsEmpty"); 363 } 364 } 365 }; 366 367 struct ExpectOk : public ExpectBase { 368 using ExpectBase::ExpectBase; 369 constexpr explicit ExpectOk(SourceLocation loc = SourceLocation::current()) 370 : ExpectBase(loc) {} 371 372 template <typename T> 373 constexpr Result<T> operator()(StatusOr<T> s) const { 374 if (s.ok()) { 375 return std::move(s).value(); 376 } else { 377 return TraceUnexpectedStatus(fcp::OK, s.status()); 378 } 379 } 380 381 Result<Unit> operator()(const Status& s) const { 382 if (s.code() == fcp::OK) { 383 return Unit{}; 384 } else { 385 return TraceUnexpectedStatus(fcp::OK, s); 386 } 387 } 388 }; 389 390 } // namespace fcp 391 392 #define FCP_TRY(...) \ 393 ({ \ 394 auto try_tmp_value_ = (__VA_ARGS__); \ 395 if (::fcp::result_internal::ResultIsError(try_tmp_value_)) { \ 396 return try_tmp_value_.GetErrorOrDie(); \ 397 } \ 398 std::move(try_tmp_value_).GetValueOrDie(); \ 399 }) 400 401 #endif // FCP_BASE_RESULT_H_ 402