xref: /aosp_15_r20/external/federated-compute/fcp/base/result.h (revision 14675a029014e728ec732f129a32e299b2da0601)
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