1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #pragma once
32
33 #include <boost/optional.hpp>
34 #include <iosfwd>
35 #include <type_traits>
36 #include <utility>
37
38 #include "mongo/base/static_assert.h"
39 #include "mongo/base/status.h"
40 #include "mongo/platform/compiler.h"
41
42 #define MONGO_INCLUDE_INVARIANT_H_WHITELISTED
43 #include "mongo/util/invariant.h"
44 #undef MONGO_INCLUDE_INVARIANT_H_WHITELISTED
45
46
47 namespace mongo {
48
49 template <typename T>
50 class StatusWith;
51
52 // Using extern constexpr to prevent the compiler from allocating storage as a poor man's c++17
53 // inline constexpr variable.
54 // TODO delete extern in c++17 because inline is the default for constexper variables.
55 template <typename T>
56 extern constexpr bool isStatusWith = false;
57 template <typename T>
58 extern constexpr bool isStatusWith<StatusWith<T>> = true;
59
60 template <typename T>
61 extern constexpr bool isStatusOrStatusWith =
62 std::is_same<T, mongo::Status>::value || isStatusWith<T>;
63
64 template <typename T>
65 using StatusOrStatusWith = std::conditional_t<std::is_void<T>::value, Status, StatusWith<T>>;
66
67 /**
68 * StatusWith is used to return an error or a value.
69 * This class is designed to make exception-free code cleaner by not needing as many out
70 * parameters.
71 *
72 * Example:
73 * StatusWith<int> fib( int n ) {
74 * if ( n < 0 )
75 * return StatusWith<int>( ErrorCodes::BadValue, "parameter to fib has to be >= 0" );
76 * if ( n <= 1 ) return StatusWith<int>( 1 );
77 * StatusWith<int> a = fib( n - 1 );
78 * StatusWith<int> b = fib( n - 2 );
79 * if ( !a.isOK() ) return a;
80 * if ( !b.isOK() ) return b;
81 * return StatusWith<int>( a.getValue() + b.getValue() );
82 * }
83 */
84 template <typename T>
85 class MONGO_WARN_UNUSED_RESULT_CLASS StatusWith {
86 MONGO_STATIC_ASSERT_MSG(!isStatusOrStatusWith<T>,
87 "StatusWith<Status> and StatusWith<StatusWith<T>> are banned.");
88
89 public:
90 using value_type = T;
91
92 /**
93 * for the error case
94 */
StatusWith(ErrorCodes::Error code,StringData reason)95 MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, StringData reason)
96 : _status(code, reason) {}
StatusWith(ErrorCodes::Error code,std::string reason)97 MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, std::string reason)
98 : _status(code, std::move(reason)) {}
StatusWith(ErrorCodes::Error code,const char * reason)99 MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, const char* reason)
100 : _status(code, reason) {}
StatusWith(ErrorCodes::Error code,const mongoutils::str::stream & reason)101 MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code,
102 const mongoutils::str::stream& reason)
103 : _status(code, reason) {}
104
105 /**
106 * for the error case
107 */
StatusWith(Status status)108 MONGO_COMPILER_COLD_FUNCTION StatusWith(Status status) : _status(std::move(status)) {
109 dassert(!isOK());
110 }
111
112 /**
113 * for the OK case
114 */
StatusWith(T t)115 StatusWith(T t) : _status(Status::OK()), _t(std::move(t)) {}
116
getValue()117 const T& getValue() const {
118 dassert(isOK());
119 return *_t;
120 }
121
getValue()122 T& getValue() {
123 dassert(isOK());
124 return *_t;
125 }
126
getStatus()127 const Status& getStatus() const {
128 return _status;
129 }
130
isOK()131 bool isOK() const {
132 return _status.isOK();
133 }
134
135
136 /**
137 * This method is a transitional tool, to facilitate transition to compile-time enforced status
138 * checking.
139 *
140 * NOTE: DO NOT ADD NEW CALLS TO THIS METHOD. This method serves the same purpose as
141 * `.getStatus().ignore()`; however, it indicates a situation where the code that presently
142 * ignores a status code has not been audited for correctness. This method will be removed at
143 * some point. If you encounter a compiler error from ignoring the result of a `StatusWith`
144 * returning function be sure to check the return value, or deliberately ignore the return
145 * value. The function is named to be auditable independently from unaudited `Status` ignore
146 * cases.
147 */
status_with_transitional_ignore()148 inline void status_with_transitional_ignore() && noexcept {};
149 inline void status_with_transitional_ignore() const& noexcept = delete;
150
151 private:
152 Status _status;
153 boost::optional<T> _t;
154 };
155
156 template <typename T, typename... Args>
makeStatusWith(Args &&...args)157 StatusWith<T> makeStatusWith(Args&&... args) {
158 return StatusWith<T>{T(std::forward<Args>(args)...)};
159 }
160
161 template <typename T>
162 std::ostream& operator<<(std::ostream& stream, const StatusWith<T>& sw) {
163 if (sw.isOK())
164 return stream << sw.getValue();
165 return stream << sw.getStatus();
166 }
167
168 //
169 // EqualityComparable(StatusWith<T>, T). Intentionally not providing an ordering relation.
170 //
171
172 template <typename T>
173 bool operator==(const StatusWith<T>& sw, const T& val) {
174 return sw.isOK() && sw.getValue() == val;
175 }
176
177 template <typename T>
178 bool operator==(const T& val, const StatusWith<T>& sw) {
179 return sw.isOK() && val == sw.getValue();
180 }
181
182 template <typename T>
183 bool operator!=(const StatusWith<T>& sw, const T& val) {
184 return !(sw == val);
185 }
186
187 template <typename T>
188 bool operator!=(const T& val, const StatusWith<T>& sw) {
189 return !(val == sw);
190 }
191
192 //
193 // EqualityComparable(StatusWith<T>, Status)
194 //
195
196 template <typename T>
197 bool operator==(const StatusWith<T>& sw, const Status& status) {
198 return sw.getStatus() == status;
199 }
200
201 template <typename T>
202 bool operator==(const Status& status, const StatusWith<T>& sw) {
203 return status == sw.getStatus();
204 }
205
206 template <typename T>
207 bool operator!=(const StatusWith<T>& sw, const Status& status) {
208 return !(sw == status);
209 }
210
211 template <typename T>
212 bool operator!=(const Status& status, const StatusWith<T>& sw) {
213 return !(status == sw);
214 }
215
216 //
217 // EqualityComparable(StatusWith<T>, ErrorCode)
218 //
219
220 template <typename T>
221 bool operator==(const StatusWith<T>& sw, const ErrorCodes::Error code) {
222 return sw.getStatus() == code;
223 }
224
225 template <typename T>
226 bool operator==(const ErrorCodes::Error code, const StatusWith<T>& sw) {
227 return code == sw.getStatus();
228 }
229
230 template <typename T>
231 bool operator!=(const StatusWith<T>& sw, const ErrorCodes::Error code) {
232 return !(sw == code);
233 }
234
235 template <typename T>
236 bool operator!=(const ErrorCodes::Error code, const StatusWith<T>& sw) {
237 return !(code == sw);
238 }
239
240 } // namespace mongo
241