1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#ifndef BUTIL_STATUS_H
19#define BUTIL_STATUS_H
20
21#include <stdarg.h> // va_list
22#include <stdlib.h> // free
23#include <string> // std::string
24#include <ostream> // std::ostream
25#include "butil/strings/string_piece.h"
26
27namespace butil {
28
29// A Status encapsulates the result of an operation. It may indicate success,
30// or it may indicate an error with an associated error message. It's suitable
31// for passing status of functions with richer information than just error_code
32// in exception-forbidden code. This utility is inspired by leveldb::Status.
33//
34// Multiple threads can invoke const methods on a Status without
35// external synchronization, but if any of the threads may call a
36// non-const method, all threads accessing the same Status must use
37// external synchronization.
38//
39// Since failed status needs to allocate memory, you should be careful when
40// failed status is frequent.
41
42class Status {
43public:
44 struct State {
45 int code;
46 unsigned size; // length of message string
47 unsigned state_size;
48 char message[0];
49 };
50
51 // Create a success status.
52 Status() : _state(NULL) { }
53 // Return a success status.
54 static Status OK() { return Status(); }
55
56 ~Status() { reset(); }
57
58 // Create a failed status.
59 // error_text is formatted from `fmt' and following arguments.
60 Status(int code, const char* fmt, ...)
61 __attribute__ ((__format__ (__printf__, 3, 4)))
62 : _state(NULL) {
63 va_list ap;
64 va_start(ap, fmt);
65 set_errorv(code, fmt, ap);
66 va_end(ap);
67 }
68 Status(int code, const butil::StringPiece& error_msg) : _state(NULL) {
69 set_error(code, error_msg);
70 }
71
72 // Copy the specified status. Internal fields are deeply copied.
73 Status(const Status& s);
74 void operator=(const Status& s);
75
76 // Reset this status to be OK.
77 void reset();
78
79 // Reset this status to be failed.
80 // Returns 0 on success, -1 otherwise and internal fields are not changed.
81 int set_error(int code, const char* error_format, ...)
82 __attribute__ ((__format__ (__printf__, 3, 4)));
83 int set_error(int code, const butil::StringPiece& error_msg);
84 int set_errorv(int code, const char* error_format, va_list args);
85
86 // Returns true iff the status indicates success.
87 bool ok() const { return (_state == NULL); }
88
89 // Get the error code
90 int error_code() const {
91 return (_state == NULL) ? 0 : _state->code;
92 }
93
94 // Return a string representation of the status.
95 // Returns "OK" for success.
96 // NOTICE:
97 // * You can print a Status to std::ostream directly
98 // * if message contains '\0', error_cstr() will not be shown fully.
99 const char* error_cstr() const {
100 return (_state == NULL ? "OK" : _state->message);
101 }
102 butil::StringPiece error_data() const {
103 return (_state == NULL ? butil::StringPiece("OK", 2)
104 : butil::StringPiece(_state->message, _state->size));
105 }
106 std::string error_str() const;
107
108 void swap(butil::Status& other) { std::swap(_state, other._state); }
109
110private:
111 // OK status has a NULL _state. Otherwise, _state is a State object
112 // converted from malloc().
113 State* _state;
114
115 static State* copy_state(const State* s);
116};
117
118inline Status::Status(const Status& s) {
119 _state = (s._state == NULL) ? NULL : copy_state(s._state);
120}
121
122inline int Status::set_error(int code, const char* msg, ...) {
123 va_list ap;
124 va_start(ap, msg);
125 const int rc = set_errorv(code, msg, ap);
126 va_end(ap);
127 return rc;
128}
129
130inline void Status::reset() {
131 free(_state);
132 _state = NULL;
133}
134
135inline void Status::operator=(const Status& s) {
136 // The following condition catches both aliasing (when this == &s),
137 // and the common case where both s and *this are ok.
138 if (_state == s._state) {
139 return;
140 }
141 if (s._state == NULL) {
142 free(_state);
143 _state = NULL;
144 } else {
145 set_error(s._state->code,
146 butil::StringPiece(s._state->message, s._state->size));
147 }
148}
149
150inline std::ostream& operator<<(std::ostream& os, const Status& st) {
151 // NOTE: don't use st.error_text() which is inaccurate if message has '\0'
152 return os << st.error_data();
153}
154
155} // namespace butil
156
157#endif // BUTIL_STATUS_H
158