Skip to content

Commit eefaea0

Browse files
committed
Separate //cuttlefish/result:expect from //cuttlefish/result
This makes the dependency on the usage macros distinct from the dependency on the support code. Bug: b/471062163
1 parent 75e9cfe commit eefaea0

File tree

3 files changed

+223
-195
lines changed

3 files changed

+223
-195
lines changed

base/cvd/cuttlefish/result/BUILD.bazel

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ cf_cc_library(
1515
)
1616

1717
cf_cc_library(
18-
name = "result",
19-
hdrs = ["result.h"],
18+
name = "expect",
19+
hdrs = ["expect.h"],
2020
deps = [
2121
"//cuttlefish/result:error_type",
2222
"//cuttlefish/result:result_type",
@@ -25,6 +25,15 @@ cf_cc_library(
2525
],
2626
)
2727

28+
cf_cc_library(
29+
name = "result",
30+
hdrs = ["result.h"],
31+
deps = [
32+
"//cuttlefish/result:expect",
33+
"//cuttlefish/result:result_type",
34+
],
35+
)
36+
2837
cf_cc_test(
2938
name = "result_test",
3039
srcs = ["result_test.cpp"],
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
#include <utility>
21+
22+
#include <android-base/format.h> // IWYU pragma: export
23+
#include <android-base/logging.h>
24+
#include <android-base/result.h> // IWYU pragma: export
25+
26+
#include "cuttlefish/result/error_type.h"
27+
#include "cuttlefish/result/result_type.h" // IWYU pragma: export
28+
29+
namespace cuttlefish {
30+
31+
/**
32+
* Error return macro that includes the location in the file in the error
33+
* message. Use CF_ERRNO when including information from errno, otherwise use
34+
* the base CF_ERR macro.
35+
*
36+
* Example usage:
37+
*
38+
* if (mkdir(path.c_str()) != 0) {
39+
* return CF_ERRNO("mkdir(\"" << path << "\") failed: "
40+
* << strerror(errno));
41+
* }
42+
*
43+
* This will return an error with the text
44+
*
45+
* mkdir(...) failed: ...
46+
* at path/to/file.cpp:50
47+
* in Result<std::string> MyFunction()
48+
*/
49+
#define CF_ERR(MSG) (CF_STACK_TRACE_ENTRY("") << MSG)
50+
#define CF_ERRNO(MSG) (CF_STACK_TRACE_ENTRY("") << MSG)
51+
#define CF_ERRF(MSG, ...) \
52+
(CF_STACK_TRACE_ENTRY("") << fmt::format(FMT_STRING(MSG), __VA_ARGS__))
53+
54+
template <typename T>
55+
T OutcomeDereference(std::optional<T>&& value) {
56+
return std::move(*value);
57+
}
58+
59+
template <typename T>
60+
typename std::conditional_t<std::is_void_v<T>, bool, T> OutcomeDereference(
61+
Result<T>&& result) {
62+
if constexpr (std::is_void<T>::value) {
63+
return result.ok();
64+
} else {
65+
return std::move(*result);
66+
}
67+
}
68+
69+
template <typename T>
70+
typename std::enable_if<std::is_convertible_v<T, bool>, T>::type
71+
OutcomeDereference(T&& value) {
72+
return std::forward<T>(value);
73+
}
74+
75+
inline bool TypeIsSuccess(bool value) { return value; }
76+
77+
template <typename T>
78+
bool TypeIsSuccess(std::optional<T>& value) {
79+
return value.has_value();
80+
}
81+
82+
template <typename T>
83+
bool TypeIsSuccess(Result<T>& value) {
84+
return value.ok();
85+
}
86+
87+
template <typename T>
88+
bool TypeIsSuccess(Result<T>&& value) {
89+
return value.ok();
90+
}
91+
92+
inline auto ErrorFromType(bool) { return StackTraceError(); }
93+
94+
template <typename T>
95+
inline auto ErrorFromType(std::optional<T>) {
96+
return StackTraceError();
97+
}
98+
99+
template <typename T>
100+
auto ErrorFromType(Result<T>& value) {
101+
return value.error();
102+
}
103+
104+
template <typename T>
105+
auto ErrorFromType(Result<T>&& value) {
106+
return value.error();
107+
}
108+
109+
#define CF_EXPECT_OVERLOAD(_1, _2, NAME, ...) NAME
110+
111+
#define CF_EXPECT2(RESULT, MSG) \
112+
({ \
113+
decltype(RESULT)&& macro_intermediate_result = RESULT; \
114+
if (!TypeIsSuccess(macro_intermediate_result)) { \
115+
auto current_entry = CF_STACK_TRACE_ENTRY(#RESULT); \
116+
current_entry << MSG; \
117+
auto error = ErrorFromType(macro_intermediate_result); \
118+
error.PushEntry(std::move(current_entry)); \
119+
return std::move(error); \
120+
}; \
121+
OutcomeDereference(std::move(macro_intermediate_result)); \
122+
})
123+
124+
#define CF_EXPECT1(RESULT) CF_EXPECT2(RESULT, "")
125+
126+
/**
127+
* Error propagation macro that can be used as an expression.
128+
*
129+
* The first argument can be either a Result or a type that is convertible to
130+
* a boolean. A successful result will return the value inside the result, or
131+
* a conversion to a `true` value will return the unconverted value. This is
132+
* useful for e.g. pointers which can be tested through boolean conversion.
133+
*
134+
* In the failure case, this macro will return from the containing function
135+
* with a failing Result. The failing result will include information about the
136+
* call site, details from the optional second argument if given, and details
137+
* from the failing inner expression if it is a Result.
138+
*
139+
* This macro must be invoked only in functions that return a Result.
140+
*
141+
* Example usage:
142+
*
143+
* Result<std::string> CreateTempDir();
144+
*
145+
* Result<std::string> CreatePopulatedTempDir() {
146+
* std::string dir = CF_EXPECT(CreateTempDir(), "Failed to create dir");
147+
* // Do something with dir
148+
* return dir;
149+
* }
150+
*
151+
* If CreateTempDir fails, the function will returna Result with an error
152+
* message that looks like
153+
*
154+
* Internal error
155+
* at /path/to/otherfile.cpp:50
156+
* in Result<std::string> CreateTempDir()
157+
* Failed to create dir
158+
* at /path/to/file.cpp:81:
159+
* in Result<std::string> CreatePopulatedTempDir()
160+
* for CF_EXPECT(CreateTempDir())
161+
*/
162+
#define CF_EXPECT(...) \
163+
CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__)
164+
165+
#define CF_EXPECTF(RESULT, MSG, ...) \
166+
CF_EXPECT(RESULT, fmt::format(FMT_STRING(MSG), __VA_ARGS__))
167+
168+
#define CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, MSG) \
169+
({ \
170+
auto&& lhs_macro_intermediate_result = LHS_RESULT; \
171+
auto&& rhs_macro_intermediate_result = RHS_RESULT; \
172+
bool comparison_result = lhs_macro_intermediate_result COMPARE_OP \
173+
rhs_macro_intermediate_result; \
174+
if (!comparison_result) { \
175+
auto current_entry = CF_STACK_TRACE_ENTRY(""); \
176+
current_entry << "Expected \"" << #LHS_RESULT << "\" " << #COMPARE_OP \
177+
<< " \"" << #RHS_RESULT << "\" but was " \
178+
<< lhs_macro_intermediate_result << " vs " \
179+
<< rhs_macro_intermediate_result << ". "; \
180+
current_entry << MSG; \
181+
auto error = ErrorFromType(false); \
182+
error.PushEntry(std::move(current_entry)); \
183+
return std::move(error); \
184+
}; \
185+
comparison_result; \
186+
})
187+
188+
#define CF_COMPARE_EXPECT3(COMPARE_OP, LHS_RESULT, RHS_RESULT) \
189+
CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, "")
190+
191+
#define CF_COMPARE_EXPECT_OVERLOAD(_1, _2, _3, _4, NAME, ...) NAME
192+
193+
#define CF_COMPARE_EXPECT(...) \
194+
CF_COMPARE_EXPECT_OVERLOAD(__VA_ARGS__, CF_COMPARE_EXPECT4, \
195+
CF_COMPARE_EXPECT3) \
196+
(__VA_ARGS__)
197+
198+
#define CF_EXPECT_EQ(LHS_RESULT, RHS_RESULT, ...) \
199+
CF_COMPARE_EXPECT(==, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
200+
#define CF_EXPECT_NE(LHS_RESULT, RHS_RESULT, ...) \
201+
CF_COMPARE_EXPECT(!=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
202+
#define CF_EXPECT_LE(LHS_RESULT, RHS_RESULT, ...) \
203+
CF_COMPARE_EXPECT(<=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
204+
#define CF_EXPECT_LT(LHS_RESULT, RHS_RESULT, ...) \
205+
CF_COMPARE_EXPECT(<, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
206+
#define CF_EXPECT_GE(LHS_RESULT, RHS_RESULT, ...) \
207+
CF_COMPARE_EXPECT(>=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
208+
#define CF_EXPECT_GT(LHS_RESULT, RHS_RESULT, ...) \
209+
CF_COMPARE_EXPECT(>, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__)
210+
211+
} // namespace cuttlefish

0 commit comments

Comments
 (0)