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 | |
27 | namespace 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 | |
42 | class Status { |
43 | public: |
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 | |
110 | private: |
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 | |
118 | inline Status::Status(const Status& s) { |
119 | _state = (s._state == NULL) ? NULL : copy_state(s._state); |
120 | } |
121 | |
122 | inline 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 | |
130 | inline void Status::reset() { |
131 | free(_state); |
132 | _state = NULL; |
133 | } |
134 | |
135 | inline 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 | |
150 | inline 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 | |