• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2022 The Android Open Source Project
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 #pragma once
17 
18 #include <optional>
19 #include <type_traits>
20 
21 #include <android-base/logging.h>
22 #include <android-base/result.h>  // IWYU pragma: export
23 
24 namespace cuttlefish {
25 
26 using android::base::Result;
27 
28 #define CF_ERR_MSG()                             \
29   "  at " << __FILE__ << ":" << __LINE__ << "\n" \
30           << "  in " << __PRETTY_FUNCTION__
31 
32 /**
33  * Error return macro that includes the location in the file in the error
34  * message. Use CF_ERRNO when including information from errno, otherwise use
35  * the base CF_ERR macro.
36  *
37  * Example usage:
38  *
39  *     if (mkdir(path.c_str()) != 0) {
40  *       return CF_ERRNO("mkdir(\"" << path << "\") failed: "
41  *                       << strerror(errno));
42  *     }
43  *
44  * This will return an error with the text
45  *
46  *     mkdir(...) failed: ...
47  *       at path/to/file.cpp:50
48  *       in Result<std::string> MyFunction()
49  */
50 #define CF_ERR(MSG) android::base::Error() << MSG << "\n" << CF_ERR_MSG()
51 #define CF_ERRNO(MSG)                        \
52   android::base::ErrnoError() << MSG << "\n" \
53                               << CF_ERR_MSG() << "\n  with errno " << errno
54 
55 template <typename T>
OutcomeDereference(std::optional<T> && value)56 T OutcomeDereference(std::optional<T>&& value) {
57   return std::move(*value);
58 }
59 
60 template <typename T>
OutcomeDereference(Result<T> && result)61 typename std::conditional_t<std::is_void_v<T>, bool, T> OutcomeDereference(
62     Result<T>&& result) {
63   if constexpr (std::is_void<T>::value) {
64     return result.ok();
65   } else {
66     return std::move(*result);
67   }
68 }
69 
70 template <typename T>
71 typename std::enable_if<std::is_convertible_v<T, bool>, T>::type
OutcomeDereference(T && value)72 OutcomeDereference(T&& value) {
73   return std::forward<T>(value);
74 }
75 
TypeIsSuccess(bool value)76 inline bool TypeIsSuccess(bool value) { return value; }
77 
78 template <typename T>
TypeIsSuccess(std::optional<T> & value)79 bool TypeIsSuccess(std::optional<T>& value) {
80   return value.has_value();
81 }
82 
83 template <typename T>
TypeIsSuccess(Result<T> & value)84 bool TypeIsSuccess(Result<T>& value) {
85   return value.ok();
86 }
87 
88 template <typename T>
TypeIsSuccess(Result<T> && value)89 bool TypeIsSuccess(Result<T>&& value) {
90   return value.ok();
91 }
92 
ErrorFromType(bool)93 inline auto ErrorFromType(bool) {
94   return (android::base::Error() << "Received `false`").str();
95 }
96 
97 template <typename T>
ErrorFromType(std::optional<T>)98 inline auto ErrorFromType(std::optional<T>) {
99   return (android::base::Error() << "Received empty optional").str();
100 }
101 
102 template <typename T>
ErrorFromType(Result<T> & value)103 auto ErrorFromType(Result<T>& value) {
104   return value.error();
105 }
106 
107 template <typename T>
ErrorFromType(Result<T> && value)108 auto ErrorFromType(Result<T>&& value) {
109   return value.error();
110 }
111 
112 #define CF_EXPECT_OVERLOAD(_1, _2, NAME, ...) NAME
113 
114 #define CF_EXPECT2(RESULT, MSG)                                  \
115   ({                                                             \
116     decltype(RESULT)&& macro_intermediate_result = RESULT;       \
117     if (!TypeIsSuccess(macro_intermediate_result)) {             \
118       return android::base::Error()                              \
119              << ErrorFromType(macro_intermediate_result) << "\n" \
120              << MSG << "\n"                                      \
121              << CF_ERR_MSG() << "\n"                             \
122              << "  for CF_EXPECT(" << #RESULT << ")";            \
123     };                                                           \
124     OutcomeDereference(std::move(macro_intermediate_result));    \
125   })
126 
127 #define CF_EXPECT1(RESULT) CF_EXPECT2(RESULT, "Received error")
128 
129 /**
130  * Error propagation macro that can be used as an expression.
131  *
132  * The first argument can be either a Result or a type that is convertible to
133  * a boolean. A successful result will return the value inside the result, or
134  * a conversion to a `true` value will return the unconverted value. This is
135  * useful for e.g. pointers which can be tested through boolean conversion.
136  *
137  * In the failure case, this macro will return from the containing function
138  * with a failing Result. The failing result will include information about the
139  * call site, details from the optional second argument if given, and details
140  * from the failing inner expression if it is a Result.
141  *
142  * This macro must be invoked only in functions that return a Result.
143  *
144  * Example usage:
145  *
146  *     Result<std::string> CreateTempDir();
147  *
148  *     Result<std::string> CreatePopulatedTempDir() {
149  *       std::string dir = CF_EXPECT(CreateTempDir(), "Failed to create dir");
150  *       // Do something with dir
151  *       return dir;
152  *     }
153  *
154  * If CreateTempDir fails, the function will returna Result with an error
155  * message that looks like
156  *
157  *      Internal error
158  *        at /path/to/otherfile.cpp:50
159  *        in Result<std::string> CreateTempDir()
160  *      Failed to create dir
161  *        at /path/to/file.cpp:81:
162  *        in Result<std::string> CreatePopulatedTempDir()
163  *        for CF_EXPECT(CreateTempDir())
164  */
165 #define CF_EXPECT(...) \
166   CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__)
167 
168 }  // namespace cuttlefish
169