• 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 class StackTraceError;
27 
28 class StackTraceEntry {
29  public:
StackTraceEntry(std::string file,size_t line,std::string pretty_function)30   StackTraceEntry(std::string file, size_t line, std::string pretty_function)
31       : file_(std::move(file)),
32         line_(line),
33         pretty_function_(std::move(pretty_function)) {}
34 
StackTraceEntry(std::string file,size_t line,std::string pretty_function,std::string expression)35   StackTraceEntry(std::string file, size_t line, std::string pretty_function,
36                   std::string expression)
37       : file_(std::move(file)),
38         line_(line),
39         pretty_function_(std::move(pretty_function)),
40         expression_(std::move(expression)) {}
41 
StackTraceEntry(const StackTraceEntry & other)42   StackTraceEntry(const StackTraceEntry& other)
43       : file_(other.file_),
44         line_(other.line_),
45         pretty_function_(other.pretty_function_),
46         expression_(other.expression_),
47         message_(other.message_.str()) {}
48 
49   StackTraceEntry(StackTraceEntry&&) = default;
50   StackTraceEntry& operator=(const StackTraceEntry& other) {
51     file_ = other.file_;
52     line_ = other.line_;
53     pretty_function_ = other.pretty_function_;
54     expression_ = other.expression_;
55     message_.str(other.message_.str());
56     return *this;
57   }
58   StackTraceEntry& operator=(StackTraceEntry&&) = default;
59 
60   template <typename T>
61   StackTraceEntry& operator<<(T&& message_ext) & {
62     message_ << std::forward<T>(message_ext);
63     return *this;
64   }
65   template <typename T>
66   StackTraceEntry operator<<(T&& message_ext) && {
67     message_ << std::forward<T>(message_ext);
68     return std::move(*this);
69   }
70 
71   operator StackTraceError() &&;
72   template <typename T>
73   operator android::base::expected<T, StackTraceError>() &&;
74 
HasMessage()75   bool HasMessage() const { return !message_.str().empty(); }
76 
Write(std::ostream & stream)77   void Write(std::ostream& stream) const { stream << message_.str(); }
WriteVerbose(std::ostream & stream)78   void WriteVerbose(std::ostream& stream) const {
79     auto str = message_.str();
80     if (str.empty()) {
81       stream << "Failure\n";
82     } else {
83       stream << message_.str() << "\n";
84     }
85     stream << " at " << file_ << ":" << line_ << "\n";
86     stream << " in " << pretty_function_;
87     if (!expression_.empty()) {
88       stream << " for CF_EXPECT(" << expression_ << ")\n";
89     }
90   }
91 
92  private:
93   std::string file_;
94   size_t line_;
95   std::string pretty_function_;
96   std::string expression_;
97   std::stringstream message_;
98 };
99 
100 #define CF_STACK_TRACE_ENTRY(expression) \
101   StackTraceEntry(__FILE__, __LINE__, __PRETTY_FUNCTION__, expression)
102 
103 class StackTraceError {
104  public:
PushEntry(StackTraceEntry entry)105   StackTraceError& PushEntry(StackTraceEntry entry) & {
106     stack_.emplace_back(std::move(entry));
107     return *this;
108   }
PushEntry(StackTraceEntry entry)109   StackTraceError PushEntry(StackTraceEntry entry) && {
110     stack_.emplace_back(std::move(entry));
111     return std::move(*this);
112   }
Stack()113   const std::vector<StackTraceEntry>& Stack() const { return stack_; }
114 
Message()115   std::string Message() const {
116     std::stringstream writer;
117     for (const auto& entry : stack_) {
118       entry.Write(writer);
119     }
120     return writer.str();
121   }
122 
Trace()123   std::string Trace() const {
124     std::stringstream writer;
125     for (const auto& entry : stack_) {
126       entry.WriteVerbose(writer);
127     }
128     return writer.str();
129   }
130 
131   template <typename T>
132   operator android::base::expected<T, StackTraceError>() && {
133     return android::base::unexpected(std::move(*this));
134   }
135 
136  private:
137   std::vector<StackTraceEntry> stack_;
138 };
139 
StackTraceError()140 inline StackTraceEntry::operator StackTraceError() && {
141   return StackTraceError().PushEntry(std::move(*this));
142 }
143 
144 template <typename T>
145 inline StackTraceEntry::operator android::base::expected<T,
146                                                          StackTraceError>() && {
147   return android::base::unexpected(std::move(*this));
148 }
149 
150 template <typename T>
151 using Result = android::base::expected<T, StackTraceError>;
152 
153 /**
154  * Error return macro that includes the location in the file in the error
155  * message. Use CF_ERRNO when including information from errno, otherwise use
156  * the base CF_ERR macro.
157  *
158  * Example usage:
159  *
160  *     if (mkdir(path.c_str()) != 0) {
161  *       return CF_ERRNO("mkdir(\"" << path << "\") failed: "
162  *                       << strerror(errno));
163  *     }
164  *
165  * This will return an error with the text
166  *
167  *     mkdir(...) failed: ...
168  *       at path/to/file.cpp:50
169  *       in Result<std::string> MyFunction()
170  */
171 #define CF_ERR(MSG) (CF_STACK_TRACE_ENTRY("") << MSG)
172 #define CF_ERRNO(MSG) (CF_STACK_TRACE_ENTRY("") << MSG)
173 
174 template <typename T>
OutcomeDereference(std::optional<T> && value)175 T OutcomeDereference(std::optional<T>&& value) {
176   return std::move(*value);
177 }
178 
179 template <typename T>
OutcomeDereference(Result<T> && result)180 typename std::conditional_t<std::is_void_v<T>, bool, T> OutcomeDereference(
181     Result<T>&& result) {
182   if constexpr (std::is_void<T>::value) {
183     return result.ok();
184   } else {
185     return std::move(*result);
186   }
187 }
188 
189 template <typename T>
190 typename std::enable_if<std::is_convertible_v<T, bool>, T>::type
OutcomeDereference(T && value)191 OutcomeDereference(T&& value) {
192   return std::forward<T>(value);
193 }
194 
TypeIsSuccess(bool value)195 inline bool TypeIsSuccess(bool value) { return value; }
196 
197 template <typename T>
TypeIsSuccess(std::optional<T> & value)198 bool TypeIsSuccess(std::optional<T>& value) {
199   return value.has_value();
200 }
201 
202 template <typename T>
TypeIsSuccess(Result<T> & value)203 bool TypeIsSuccess(Result<T>& value) {
204   return value.ok();
205 }
206 
207 template <typename T>
TypeIsSuccess(Result<T> && value)208 bool TypeIsSuccess(Result<T>&& value) {
209   return value.ok();
210 }
211 
ErrorFromType(bool)212 inline auto ErrorFromType(bool) { return StackTraceError(); }
213 
214 template <typename T>
ErrorFromType(std::optional<T>)215 inline auto ErrorFromType(std::optional<T>) {
216   return StackTraceError();
217 }
218 
219 template <typename T>
ErrorFromType(Result<T> & value)220 auto ErrorFromType(Result<T>& value) {
221   return value.error();
222 }
223 
224 template <typename T>
ErrorFromType(Result<T> && value)225 auto ErrorFromType(Result<T>&& value) {
226   return value.error();
227 }
228 
229 #define CF_EXPECT_OVERLOAD(_1, _2, NAME, ...) NAME
230 
231 #define CF_EXPECT2(RESULT, MSG)                               \
232   ({                                                          \
233     decltype(RESULT)&& macro_intermediate_result = RESULT;    \
234     if (!TypeIsSuccess(macro_intermediate_result)) {          \
235       auto current_entry = CF_STACK_TRACE_ENTRY(#RESULT);     \
236       current_entry << MSG;                                   \
237       auto error = ErrorFromType(macro_intermediate_result);  \
238       error.PushEntry(std::move(current_entry));              \
239       return error;                                           \
240     };                                                        \
241     OutcomeDereference(std::move(macro_intermediate_result)); \
242   })
243 
244 #define CF_EXPECT1(RESULT) CF_EXPECT2(RESULT, "")
245 
246 /**
247  * Error propagation macro that can be used as an expression.
248  *
249  * The first argument can be either a Result or a type that is convertible to
250  * a boolean. A successful result will return the value inside the result, or
251  * a conversion to a `true` value will return the unconverted value. This is
252  * useful for e.g. pointers which can be tested through boolean conversion.
253  *
254  * In the failure case, this macro will return from the containing function
255  * with a failing Result. The failing result will include information about the
256  * call site, details from the optional second argument if given, and details
257  * from the failing inner expression if it is a Result.
258  *
259  * This macro must be invoked only in functions that return a Result.
260  *
261  * Example usage:
262  *
263  *     Result<std::string> CreateTempDir();
264  *
265  *     Result<std::string> CreatePopulatedTempDir() {
266  *       std::string dir = CF_EXPECT(CreateTempDir(), "Failed to create dir");
267  *       // Do something with dir
268  *       return dir;
269  *     }
270  *
271  * If CreateTempDir fails, the function will returna Result with an error
272  * message that looks like
273  *
274  *      Internal error
275  *        at /path/to/otherfile.cpp:50
276  *        in Result<std::string> CreateTempDir()
277  *      Failed to create dir
278  *        at /path/to/file.cpp:81:
279  *        in Result<std::string> CreatePopulatedTempDir()
280  *        for CF_EXPECT(CreateTempDir())
281  */
282 #define CF_EXPECT(...) \
283   CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__)
284 
285 #define CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, MSG)         \
286   ({                                                                        \
287     auto&& lhs_macro_intermediate_result = LHS_RESULT;                      \
288     auto&& rhs_macro_intermediate_result = RHS_RESULT;                      \
289     bool comparison_result = lhs_macro_intermediate_result COMPARE_OP       \
290         rhs_macro_intermediate_result;                                      \
291     if (!comparison_result) {                                               \
292       auto current_entry = CF_STACK_TRACE_ENTRY("");                        \
293       current_entry << "Expected \"" << #LHS_RESULT << "\" " << #COMPARE_OP \
294                     << " \"" << #RHS_RESULT << "\" but was "                \
295                     << lhs_macro_intermediate_result << " vs "              \
296                     << rhs_macro_intermediate_result << ".";                \
297       current_entry << MSG;                                                 \
298       auto error = ErrorFromType(false);                                    \
299       error.PushEntry(std::move(current_entry));                            \
300       return error;                                                         \
301     };                                                                      \
302     comparison_result;                                                      \
303   })
304 
305 #define CF_COMPARE_EXPECT3(COMPARE_OP, LHS_RESULT, RHS_RESULT) \
306   CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, "")
307 
308 #define CF_COMPARE_EXPECT_OVERLOAD(_1, _2, _3, _4, NAME, ...) NAME
309 
310 #define CF_COMPARE_EXPECT(...)                                \
311   CF_COMPARE_EXPECT_OVERLOAD(__VA_ARGS__, CF_COMPARE_EXPECT4, \
312                              CF_COMPARE_EXPECT3)              \
313   (__VA_ARGS__)
314 
315 #define CF_EXPECT_EQ(LHS_RESULT, RHS_RESULT, ...) \
316   CF_COMPARE_EXPECT(==, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
317 #define CF_EXPECT_NE(LHS_RESULT, RHS_RESULT, ...) \
318   CF_COMPARE_EXPECT(!=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
319 #define CF_EXPECT_LE(LHS_RESULT, RHS_RESULT, ...) \
320   CF_COMPARE_EXPECT(<=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
321 #define CF_EXPECT_LT(LHS_RESULT, RHS_RESULT, ...) \
322   CF_COMPARE_EXPECT(<, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
323 #define CF_EXPECT_GE(LHS_RESULT, RHS_RESULT, ...) \
324   CF_COMPARE_EXPECT(>=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
325 #define CF_EXPECT_GT(LHS_RESULT, RHS_RESULT, ...) \
326   CF_COMPARE_EXPECT(>, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
327 
328 }  // namespace cuttlefish
329