1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// StatusOr<T> is the union of a Status object and a T
32// object. StatusOr models the concept of an object that is either a
33// usable value, or an error Status explaining why such a value is
34// not present. To this end, StatusOr<T> does not allow its Status
35// value to be OkStatus(). Further, StatusOr<T*> does not allow the
36// contained pointer to be nullptr.
37//
38// The primary use-case for StatusOr<T> is as the return value of a
39// function which may fail.
40//
41// Example client usage for a StatusOr<T>, where T is not a pointer:
42//
43// StatusOr<float> result = DoBigCalculationThatCouldFail();
44// if (result.ok()) {
45// float answer = result.value();
46// printf("Big calculation yielded: %f", answer);
47// } else {
48// LOG(ERROR) << result.status();
49// }
50//
51// Example client usage for a StatusOr<T*>:
52//
53// StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
54// if (result.ok()) {
55// std::unique_ptr<Foo> foo(result.value());
56// foo->DoSomethingCool();
57// } else {
58// LOG(ERROR) << result.status();
59// }
60//
61// Example factory implementation returning StatusOr<T*>:
62//
63// StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
64// if (arg <= 0) {
65// return InvalidArgumentError("Arg must be positive");
66// } else {
67// return new Foo(arg);
68// }
69// }
70//
71
72#ifndef GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
73#define GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
74
75#include <new>
76#include <string>
77#include <utility>
78
79#include <google/protobuf/stubs/status.h>
80
81#include <google/protobuf/port_def.inc>
82
83namespace google {
84namespace protobuf {
85namespace util {
86namespace statusor_internal {
87
88template<typename T>
89class StatusOr {
90 template<typename U> friend class StatusOr;
91
92 public:
93 using value_type = T;
94
95 // Construct a new StatusOr with Status::UNKNOWN status.
96 // Construct a new StatusOr with UnknownError() status.
97 explicit StatusOr();
98
99 // Construct a new StatusOr with the given non-ok status. After calling
100 // this constructor, calls to value() will CHECK-fail.
101 //
102 // NOTE: Not explicit - we want to use StatusOr<T> as a return
103 // value, so it is convenient and sensible to be able to do 'return
104 // Status()' when the return type is StatusOr<T>.
105 //
106 // REQUIRES: status != OkStatus(). This requirement is DCHECKed.
107 // In optimized builds, passing OkStatus() here will have the effect
108 // of passing PosixErrorSpace::EINVAL as a fallback.
109 StatusOr(const Status& status); // NOLINT
110
111 // Construct a new StatusOr with the given value. If T is a plain pointer,
112 // value must not be nullptr. After calling this constructor, calls to
113 // value() will succeed, and calls to status() will return OK.
114 //
115 // NOTE: Not explicit - we want to use StatusOr<T> as a return type
116 // so it is convenient and sensible to be able to do 'return T()'
117 // when when the return type is StatusOr<T>.
118 //
119 // REQUIRES: if T is a plain pointer, value != nullptr. This requirement is
120 // DCHECKed. In optimized builds, passing a null pointer here will have
121 // the effect of passing PosixErrorSpace::EINVAL as a fallback.
122 StatusOr(const T& value); // NOLINT
123
124 // Copy constructor.
125 StatusOr(const StatusOr& other);
126
127 // Conversion copy constructor, T must be copy constructible from U
128 template<typename U>
129 StatusOr(const StatusOr<U>& other);
130
131 // Assignment operator.
132 StatusOr& operator=(const StatusOr& other);
133
134 // Conversion assignment operator, T must be assignable from U
135 template<typename U>
136 StatusOr& operator=(const StatusOr<U>& other);
137
138 // Returns a reference to our status. If this contains a T, then
139 // returns OkStatus().
140 const Status& status() const;
141
142 // Returns this->status().ok()
143 bool ok() const;
144
145 // Returns a reference to our current value, or CHECK-fails if !this->ok().
146 const T& value () const;
147
148 private:
149 Status status_;
150 T value_;
151};
152
153////////////////////////////////////////////////////////////////////////////////
154// Implementation details for StatusOr<T>
155
156class PROTOBUF_EXPORT StatusOrHelper {
157 public:
158 // Move type-agnostic error handling to the .cc.
159 static void Crash(const util::Status& status);
160
161 // Customized behavior for StatusOr<T> vs. StatusOr<T*>
162 template<typename T>
163 struct Specialize;
164};
165
166template<typename T>
167struct StatusOrHelper::Specialize {
168 // For non-pointer T, a reference can never be nullptr.
169 static inline bool IsValueNull(const T& /*t*/) { return false; }
170};
171
172template<typename T>
173struct StatusOrHelper::Specialize<T*> {
174 static inline bool IsValueNull(const T* t) { return t == nullptr; }
175};
176
177template <typename T>
178inline StatusOr<T>::StatusOr() : status_(util::UnknownError(message: "")) {}
179
180template<typename T>
181inline StatusOr<T>::StatusOr(const Status& status) {
182 if (status.ok()) {
183 status_ = util::InternalError(message: "OkStatus() is not a valid argument.");
184 } else {
185 status_ = status;
186 }
187}
188
189template<typename T>
190inline StatusOr<T>::StatusOr(const T& value) {
191 if (StatusOrHelper::Specialize<T>::IsValueNull(value)) {
192 status_ = util::InternalError(message: "nullptr is not a valid argument.");
193 } else {
194 status_ = util::OkStatus();
195 value_ = value;
196 }
197}
198
199template<typename T>
200inline StatusOr<T>::StatusOr(const StatusOr<T>& other)
201 : status_(other.status_), value_(other.value_) {
202}
203
204template<typename T>
205inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<T>& other) {
206 status_ = other.status_;
207 value_ = other.value_;
208 return *this;
209}
210
211template<typename T>
212template<typename U>
213inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
214 : status_(other.status_), value_(other.status_.ok() ? other.value_ : T()) {
215}
216
217template<typename T>
218template<typename U>
219inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
220 status_ = other.status_;
221 if (status_.ok()) value_ = other.value_;
222 return *this;
223}
224
225template<typename T>
226inline const Status& StatusOr<T>::status() const {
227 return status_;
228}
229
230template<typename T>
231inline bool StatusOr<T>::ok() const {
232 return status().ok();
233}
234
235template<typename T>
236inline const T& StatusOr<T>::value() const {
237 if (!status_.ok()) {
238 StatusOrHelper::Crash(status: status_);
239 }
240 return value_;
241}
242
243} // namespace statusor_internal
244
245using ::google::protobuf::util::statusor_internal::StatusOr;
246
247} // namespace util
248} // namespace protobuf
249} // namespace google
250
251#include <google/protobuf/port_undef.inc>
252
253#endif // GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
254