deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #ifndef API_RTCERROR_H_ |
| 12 | #define API_RTCERROR_H_ |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 13 | |
| 14 | #include <ostream> |
| 15 | #include <string> |
| 16 | #include <utility> // For std::move. |
| 17 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 18 | #include "rtc_base/checks.h" |
| 19 | #include "rtc_base/logging.h" |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 20 | |
| 21 | namespace webrtc { |
| 22 | |
| 23 | // Enumeration to represent distinct classes of errors that an application |
| 24 | // may wish to act upon differently. These roughly map to DOMExceptions or |
| 25 | // RTCError "errorDetailEnum" values in the web API, as described in the |
| 26 | // comments below. |
| 27 | enum class RTCErrorType { |
| 28 | // No error. |
| 29 | NONE, |
| 30 | |
| 31 | // An operation is valid, but currently unsupported. |
| 32 | // Maps to OperationError DOMException. |
| 33 | UNSUPPORTED_OPERATION, |
| 34 | |
| 35 | // A supplied parameter is valid, but currently unsupported. |
| 36 | // Maps to OperationError DOMException. |
| 37 | UNSUPPORTED_PARAMETER, |
| 38 | |
| 39 | // General error indicating that a supplied parameter is invalid. |
| 40 | // Maps to InvalidAccessError or TypeError DOMException depending on context. |
| 41 | INVALID_PARAMETER, |
| 42 | |
| 43 | // Slightly more specific than INVALID_PARAMETER; a parameter's value was |
| 44 | // outside the allowed range. |
| 45 | // Maps to RangeError DOMException. |
| 46 | INVALID_RANGE, |
| 47 | |
| 48 | // Slightly more specific than INVALID_PARAMETER; an error occurred while |
| 49 | // parsing string input. |
| 50 | // Maps to SyntaxError DOMException. |
| 51 | SYNTAX_ERROR, |
| 52 | |
| 53 | // The object does not support this operation in its current state. |
| 54 | // Maps to InvalidStateError DOMException. |
| 55 | INVALID_STATE, |
| 56 | |
| 57 | // An attempt was made to modify the object in an invalid way. |
| 58 | // Maps to InvalidModificationError DOMException. |
| 59 | INVALID_MODIFICATION, |
| 60 | |
| 61 | // An error occurred within an underlying network protocol. |
| 62 | // Maps to NetworkError DOMException. |
| 63 | NETWORK_ERROR, |
| 64 | |
| 65 | // Some resource has been exhausted; file handles, hardware resources, ports, |
| 66 | // etc. |
| 67 | // Maps to OperationError DOMException. |
| 68 | RESOURCE_EXHAUSTED, |
| 69 | |
| 70 | // The operation failed due to an internal error. |
| 71 | // Maps to OperationError DOMException. |
| 72 | INTERNAL_ERROR, |
| 73 | }; |
| 74 | |
| 75 | // Roughly corresponds to RTCError in the web api. Holds an error type, a |
| 76 | // message, and possibly additional information specific to that error. |
| 77 | // |
| 78 | // Doesn't contain anything beyond a type and message now, but will in the |
| 79 | // future as more errors are implemented. |
| 80 | class RTCError { |
| 81 | public: |
| 82 | // Constructors. |
| 83 | |
| 84 | // Creates a "no error" error. |
| 85 | RTCError() {} |
| 86 | explicit RTCError(RTCErrorType type) : type_(type) {} |
| 87 | // For performance, prefer using the constructor that takes a const char* if |
| 88 | // the message is a static string. |
| 89 | RTCError(RTCErrorType type, const char* message) |
| 90 | : type_(type), static_message_(message), have_string_message_(false) {} |
| 91 | RTCError(RTCErrorType type, std::string&& message) |
| 92 | : type_(type), string_message_(message), have_string_message_(true) {} |
| 93 | |
| 94 | // Delete the copy constructor and assignment operator; there aren't any use |
| 95 | // cases where you should need to copy an RTCError, as opposed to moving it. |
| 96 | // Can revisit this decision if use cases arise in the future. |
| 97 | RTCError(const RTCError& other) = delete; |
| 98 | RTCError& operator=(const RTCError& other) = delete; |
| 99 | |
| 100 | // Move constructor and move-assignment operator. |
| 101 | RTCError(RTCError&& other); |
| 102 | RTCError& operator=(RTCError&& other); |
| 103 | |
| 104 | ~RTCError(); |
| 105 | |
| 106 | // Identical to default constructed error. |
| 107 | // |
| 108 | // Preferred over the default constructor for code readability. |
| 109 | static RTCError OK(); |
| 110 | |
| 111 | // Error type. |
| 112 | RTCErrorType type() const { return type_; } |
| 113 | void set_type(RTCErrorType type) { type_ = type; } |
| 114 | |
| 115 | // Human-readable message describing the error. Shouldn't be used for |
| 116 | // anything but logging/diagnostics, since messages are not guaranteed to be |
| 117 | // stable. |
| 118 | const char* message() const; |
| 119 | // For performance, prefer using the method that takes a const char* if the |
| 120 | // message is a static string. |
| 121 | void set_message(const char* message); |
| 122 | void set_message(std::string&& message); |
| 123 | |
| 124 | // Convenience method for situations where you only care whether or not an |
| 125 | // error occurred. |
| 126 | bool ok() const { return type_ == RTCErrorType::NONE; } |
| 127 | |
| 128 | private: |
| 129 | RTCErrorType type_ = RTCErrorType::NONE; |
| 130 | // For performance, we use static strings wherever possible. But in some |
| 131 | // cases the error string may need to be constructed, in which case an |
| 132 | // std::string is used. |
| 133 | union { |
| 134 | const char* static_message_ = ""; |
| 135 | std::string string_message_; |
| 136 | }; |
| 137 | // Whether or not |static_message_| or |string_message_| is being used in the |
| 138 | // above union. |
| 139 | bool have_string_message_ = false; |
| 140 | }; |
| 141 | |
| 142 | // Outputs the error as a friendly string. Update this method when adding a new |
| 143 | // error type. |
| 144 | // |
| 145 | // Only intended to be used for logging/disagnostics. |
| 146 | std::ostream& operator<<(std::ostream& stream, RTCErrorType error); |
| 147 | |
| 148 | // Helper macro that can be used by implementations to create an error with a |
| 149 | // message and log it. |message| should be a string literal or movable |
| 150 | // std::string. |
| 151 | #define LOG_AND_RETURN_ERROR_EX(type, message, severity) \ |
| 152 | { \ |
| 153 | RTC_DCHECK(type != RTCErrorType::NONE); \ |
| 154 | LOG(severity) << message << " (" << type << ")"; \ |
| 155 | return webrtc::RTCError(type, message); \ |
| 156 | } |
| 157 | |
| 158 | #define LOG_AND_RETURN_ERROR(type, message) \ |
| 159 | LOG_AND_RETURN_ERROR_EX(type, message, LS_ERROR) |
| 160 | |
| 161 | // RTCErrorOr<T> is the union of an RTCError object and a T object. RTCErrorOr |
| 162 | // models the concept of an object that is either a usable value, or an error |
| 163 | // Status explaining why such a value is not present. To this end RTCErrorOr<T> |
| 164 | // does not allow its RTCErrorType value to be RTCErrorType::NONE. This is |
| 165 | // enforced by a debug check in most cases. |
| 166 | // |
| 167 | // The primary use-case for RTCErrorOr<T> is as the return value of a function |
| 168 | // which may fail. For example, CreateRtpSender will fail if the parameters |
| 169 | // could not be successfully applied at the media engine level, but if |
| 170 | // successful will return a unique_ptr to an RtpSender. |
| 171 | // |
| 172 | // Example client usage for a RTCErrorOr<std::unique_ptr<T>>: |
| 173 | // |
| 174 | // RTCErrorOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg); |
| 175 | // if (result.ok()) { |
| 176 | // std::unique_ptr<Foo> foo = result.ConsumeValue(); |
| 177 | // foo->DoSomethingCool(); |
| 178 | // } else { |
| 179 | // LOG(LS_ERROR) << result.error(); |
| 180 | // } |
| 181 | // |
| 182 | // Example factory implementation returning RTCErrorOr<std::unique_ptr<T>>: |
| 183 | // |
| 184 | // RTCErrorOr<std::unique_ptr<Foo>> FooFactory::MakeNewFoo(int arg) { |
| 185 | // if (arg <= 0) { |
| 186 | // return RTCError(RTCErrorType::INVALID_RANGE, "Arg must be positive"); |
| 187 | // } else { |
| 188 | // return std::unique_ptr<Foo>(new Foo(arg)); |
| 189 | // } |
| 190 | // } |
| 191 | // |
| 192 | template <typename T> |
| 193 | class RTCErrorOr { |
| 194 | // Used to convert between RTCErrorOr<Foo>/RtcErrorOr<Bar>, when an implicit |
| 195 | // conversion from Foo to Bar exists. |
| 196 | template <typename U> |
| 197 | friend class RTCErrorOr; |
| 198 | |
| 199 | public: |
| 200 | typedef T element_type; |
| 201 | |
| 202 | // Constructs a new RTCErrorOr with RTCErrorType::INTERNAL_ERROR error. This |
| 203 | // is marked 'explicit' to try to catch cases like 'return {};', where people |
| 204 | // think RTCErrorOr<std::vector<int>> will be initialized with an empty |
| 205 | // vector, instead of a RTCErrorType::INTERNAL_ERROR error. |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 206 | RTCErrorOr() : error_(RTCErrorType::INTERNAL_ERROR) {} |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 207 | |
| 208 | // Constructs a new RTCErrorOr with the given non-ok error. After calling |
| 209 | // this constructor, calls to value() will DCHECK-fail. |
| 210 | // |
| 211 | // NOTE: Not explicit - we want to use RTCErrorOr<T> as a return |
| 212 | // value, so it is convenient and sensible to be able to do 'return |
| 213 | // RTCError(...)' when the return type is RTCErrorOr<T>. |
| 214 | // |
| 215 | // REQUIRES: !error.ok(). This requirement is DCHECKed. |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 216 | RTCErrorOr(RTCError&& error) : error_(std::move(error)) { // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 217 | RTC_DCHECK(!error.ok()); |
| 218 | } |
| 219 | |
| 220 | // Constructs a new RTCErrorOr with the given value. After calling this |
| 221 | // constructor, calls to value() will succeed, and calls to error() will |
| 222 | // return a default-constructed RTCError. |
| 223 | // |
| 224 | // NOTE: Not explicit - we want to use RTCErrorOr<T> as a return type |
| 225 | // so it is convenient and sensible to be able to do 'return T()' |
| 226 | // when the return type is RTCErrorOr<T>. |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 227 | RTCErrorOr(T&& value) : value_(std::move(value)) {} // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 228 | |
| 229 | // Delete the copy constructor and assignment operator; there aren't any use |
| 230 | // cases where you should need to copy an RTCErrorOr, as opposed to moving |
| 231 | // it. Can revisit this decision if use cases arise in the future. |
| 232 | RTCErrorOr(const RTCErrorOr& other) = delete; |
| 233 | RTCErrorOr& operator=(const RTCErrorOr& other) = delete; |
| 234 | |
| 235 | // Move constructor and move-assignment operator. |
deadbeef | b5388d7 | 2017-02-24 01:17:43 -0800 | [diff] [blame] | 236 | // |
| 237 | // Visual Studio doesn't support "= default" with move constructors or |
| 238 | // assignment operators (even though they compile, they segfault), so define |
| 239 | // them explicitly. |
| 240 | RTCErrorOr(RTCErrorOr&& other) |
| 241 | : error_(std::move(other.error_)), value_(std::move(other.value_)) {} |
| 242 | RTCErrorOr& operator=(RTCErrorOr&& other) { |
| 243 | error_ = std::move(other.error_); |
| 244 | value_ = std::move(other.value_); |
| 245 | return *this; |
| 246 | } |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 247 | |
| 248 | // Conversion constructor and assignment operator; T must be copy or move |
| 249 | // constructible from U. |
| 250 | template <typename U> |
oprypin | 8e58d65 | 2017-03-21 07:52:41 -0700 | [diff] [blame] | 251 | RTCErrorOr(RTCErrorOr<U> other) // NOLINT |
deadbeef | 6038e97 | 2017-02-16 23:31:33 -0800 | [diff] [blame] | 252 | : error_(std::move(other.error_)), value_(std::move(other.value_)) {} |
| 253 | template <typename U> |
| 254 | RTCErrorOr& operator=(RTCErrorOr<U> other) { |
| 255 | error_ = std::move(other.error_); |
| 256 | value_ = std::move(other.value_); |
| 257 | return *this; |
| 258 | } |
| 259 | |
| 260 | // Returns a reference to our error. If this contains a T, then returns |
| 261 | // default-constructed RTCError. |
| 262 | const RTCError& error() const { return error_; } |
| 263 | |
| 264 | // Moves the error. Can be useful if, say "CreateFoo" returns an |
| 265 | // RTCErrorOr<Foo>, and internally calls "CreateBar" which returns an |
| 266 | // RTCErrorOr<Bar>, and wants to forward the error up the stack. |
| 267 | RTCError MoveError() { return std::move(error_); } |
| 268 | |
| 269 | // Returns this->error().ok() |
| 270 | bool ok() const { return error_.ok(); } |
| 271 | |
| 272 | // Returns a reference to our current value, or DCHECK-fails if !this->ok(). |
| 273 | // |
| 274 | // Can be convenient for the implementation; for example, a method may want |
| 275 | // to access the value in some way before returning it to the next method on |
| 276 | // the stack. |
| 277 | const T& value() const { |
| 278 | RTC_DCHECK(ok()); |
| 279 | return value_; |
| 280 | } |
| 281 | T& value() { |
| 282 | RTC_DCHECK(ok()); |
| 283 | return value_; |
| 284 | } |
| 285 | |
| 286 | // Moves our current value out of this object and returns it, or DCHECK-fails |
| 287 | // if !this->ok(). |
| 288 | T MoveValue() { |
| 289 | RTC_DCHECK(ok()); |
| 290 | return std::move(value_); |
| 291 | } |
| 292 | |
| 293 | private: |
| 294 | RTCError error_; |
| 295 | T value_; |
| 296 | }; |
| 297 | |
| 298 | } // namespace webrtc |
| 299 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 300 | #endif // API_RTCERROR_H_ |