1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 #include <cpp-string/string_printf.h> 17 #include <lib/fit/result.h> 18 19 #include <type_traits> 20 #include <variant> 21 22 #include "pw_bluetooth_sapphire/internal/host/common/assert.h" 23 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h" 24 #include "pw_bluetooth_sapphire/internal/host/common/log.h" 25 26 #pragma clang diagnostic ignored "-Wvariadic-macros" 27 #pragma clang diagnostic ignored \ 28 "-Wgnu-statement-expression-from-macro-expansion" 29 30 namespace bt { 31 32 // Type used to hold either a HostError or a ProtocolErrorCode, a 33 // protocol-defined code. This can not be constructed in such a way to represent 34 // a success or to contain the product of a successful operation, but to be used 35 // as the error type parameter of a generic result type like 36 // fit::result<Error<…>> or fit::result<Error<…>, V>. 37 // 38 // Errors can be directly constructed from HostErrors. ProtocolErrorCodes whose 39 // possible values all represent errors can also be used to construct Errors. 40 // Otherwise, ProtocolErrorCodes like that used by HCI must be converted using 41 // ToResult in order to capture success values. 42 template <typename ProtocolErrorCode> 43 class Error; 44 45 // Required trait for ProtocolErrorCode types. 46 template <typename ProtocolErrorCode> 47 struct ProtocolErrorTraits { 48 // Returns a string representation of the given ProtocolErrorCode value. 49 static std::string ToString(ProtocolErrorCode); 50 51 // Optional: returns true if the given ProtocolErrorCode value represents 52 // success. If no such value exists, do not declare this static function in 53 // the specialization. static constexpr bool is_success(ProtocolErrorCode); 54 }; 55 56 // Marker used to indicate that an Error holds only HostError. 57 class NoProtocolError { 58 constexpr NoProtocolError() = delete; 59 }; 60 61 template <> 62 struct ProtocolErrorTraits<NoProtocolError> { 63 // This won't be called but still needs to be stubbed out to link correctly. 64 static std::string ToString(NoProtocolError) { 65 BT_ASSERT(false); 66 return std::string(); 67 } 68 }; 69 70 namespace detail { 71 72 // Detects whether the given expression implicitly converts to a bt::Error. 73 template <typename T, typename = void> 74 struct IsError : std::false_type {}; 75 76 // This specialization is used when 77 // 1. T can be deduced as a template template (i.e. T<U>) 78 // 2. A function that takes Error<U> would accept a T<U>&& value as its 79 // parameter 80 template <template <typename> class T, typename U> 81 struct IsError<T<U>, 82 std::void_t<decltype(std::declval<void (&)(Error<U>)>()( 83 std::declval<T<U>>()))>> : std::true_type{}; 84 85 template <typename T> 86 constexpr bool IsErrorV = IsError<T>::value; 87 88 // Detects whether ProtocolErrorTraits<ProtocolErrorCode>::is_success has been 89 // declared. 90 template <typename ProtocolErrorCode, typename = void> 91 struct CanRepresentSuccess : std::false_type {}; 92 93 template <typename ProtocolErrorCode> 94 struct CanRepresentSuccess< 95 ProtocolErrorCode, 96 std::void_t<decltype(ProtocolErrorTraits<ProtocolErrorCode>::is_success( 97 std::declval<ProtocolErrorCode>()))>> : std::true_type{}; 98 99 template <typename ProtocolErrorCode> 100 constexpr bool CanRepresentSuccessV = 101 CanRepresentSuccess<ProtocolErrorCode>::value; 102 103 } // namespace detail 104 105 // Create a fit::result<Error<…>> from a HostError. The template parameter may 106 // be omitted to default to an fit::result<Error<NoProtocolError>> in the case 107 // that it's not useful to specify the kind of protocol error that the result 108 // could hold instead. 109 template <typename ProtocolErrorCode = NoProtocolError> 110 [[nodiscard]] constexpr fit::result<Error<ProtocolErrorCode>> ToResult( 111 HostError host_error) { 112 return fit::error(Error<ProtocolErrorCode>(host_error)); 113 } 114 115 // Create a fit::result<Error<…>> from a protocol error. 116 // This overload doesn't collide with the above when instantiated with 117 // <HostError>, because this would try to construct an invalid Error<HostError>. 118 template <typename ProtocolErrorCode> 119 [[nodiscard]] constexpr fit::result<Error<ProtocolErrorCode>> ToResult( 120 ProtocolErrorCode proto_error) { 121 if constexpr (detail::CanRepresentSuccessV<ProtocolErrorCode>) { 122 if (ProtocolErrorTraits<ProtocolErrorCode>::is_success(proto_error)) { 123 return fit::success(); 124 } 125 } 126 return fit::error(Error(std::move(proto_error))); 127 } 128 129 template <typename ProtocolErrorCode = NoProtocolError> 130 class [[nodiscard]] Error { 131 static_assert(!std::is_same_v<HostError, ProtocolErrorCode>, 132 "HostError can not be a protocol error"); 133 static_assert(!detail::IsErrorV<ProtocolErrorCode>, 134 "ProtocolErrorCode can not be a bt::Error"); 135 136 public: 137 Error() = delete; 138 ~Error() = default; 139 constexpr Error(const Error&) = default; 140 constexpr Error(Error&&) noexcept = default; 141 constexpr Error& operator=(const Error&) = default; 142 constexpr Error& operator=(Error&&) noexcept = default; 143 144 constexpr explicit Error(const HostError& host_error) : error_(host_error) {} 145 146 // This is disabled if ProtocolErrorCode may hold a value that means success, 147 // leaving only the private ctor. Instead use ToResult(ProtocolErrorCode), 148 // whose return value may hold success. 149 template <typename T = ProtocolErrorCode, 150 std::enable_if_t<!detail::CanRepresentSuccessV<T>, int> = 0> 151 constexpr explicit Error(const ProtocolErrorCode& proto_error) 152 : error_(proto_error) {} 153 154 // Intentionally implicit conversion from Error<NoProtocolError> that holds 155 // only HostErrors. This allows any Error<…> to be compared to an 156 // Error<NoProtocolError>'s HostError payload. Also, functions that accept 157 // Error<…> will take Error<NoProtocolError> without an explicit conversion. 158 // 159 // Example: 160 // void Foo(Error<BarErrorCode>); 161 // Foo(ToResult(HostError::kTimedOut)); // Compiles without having to write 162 // BarErrorCode 163 // 164 // For safety, this implicit conversion does not "chain" to allow bare 165 // ProtocolErrorCodes or HostErrors to be converted into Error or fit::result. 166 // 167 // The seemingly-extraneous template parameter serves to disable this overload 168 // when |*this| is an Error<NoProtocolError> 169 template <typename T = ProtocolErrorCode, 170 std::enable_if_t<Error<T>::may_hold_protocol_error(), int> = 0> 171 // NOLINTNEXTLINE(google-explicit-constructor) 172 constexpr Error(const Error<NoProtocolError>& other) 173 : error_(other.host_error()) {} 174 175 // Evaluates to true if and only if both Errors hold the same class of error 176 // (host vs protocol) and their codes match in value. Errors with different 177 // ProtocolErrorCodes are comparable only if their ProtocolErrorCodes have a 178 // defined operator==, which is generally not the case if the codes are 179 // strongly-type "enum class" enumerations. 180 template <typename RErrorCode> 181 constexpr bool operator==(const Error<RErrorCode>& rhs) const { 182 auto proto_error_visitor = [&](ProtocolErrorCode held) { 183 if constexpr (may_hold_protocol_error() && 184 Error<RErrorCode>::may_hold_protocol_error()) { 185 return held == rhs.protocol_error(); 186 } else { 187 // This unreachable branch makes comparisons to Error<NoProtocolError> 188 // well-defined, so that the lambda compiles as long as the protocol 189 // error codes are comparable. 190 return false; 191 } 192 }; 193 if (is_host_error() != rhs.is_host_error()) { 194 return false; 195 } 196 return Visit([&rhs](HostError held) { return held == rhs.host_error(); }, 197 proto_error_visitor); 198 } 199 200 template <typename RErrorCode> 201 constexpr bool operator!=(const Error<RErrorCode>& rhs) const { 202 return !(*this == rhs); 203 } 204 205 [[nodiscard]] std::string ToString() const { 206 return Visit( 207 [](HostError held) { return HostErrorToString(held); }, 208 [](ProtocolErrorCode held) { 209 return ProtocolErrorTraits<ProtocolErrorCode>::ToString(held); 210 }); 211 } 212 213 [[nodiscard]] constexpr bool is_host_error() const { 214 return std::holds_alternative<HostError>(error_); 215 } 216 217 [[nodiscard]] constexpr bool is_protocol_error() const { 218 return std::holds_alternative<ProtocolErrorCode>(error_); 219 } 220 221 [[nodiscard]] constexpr HostError host_error() const { 222 BT_ASSERT_MSG(is_host_error(), "Does not hold HostError"); 223 return std::get<HostError>(error_); 224 } 225 226 [[nodiscard]] constexpr ProtocolErrorCode protocol_error() const { 227 BT_ASSERT_MSG(is_protocol_error(), "Does not hold protocol error"); 228 return std::get<ProtocolErrorCode>(error_); 229 } 230 231 [[nodiscard]] constexpr bool is(ProtocolErrorCode proto_error) const { 232 return Visit( 233 [](HostError) { return false; }, 234 [proto_error](ProtocolErrorCode held) { return held == proto_error; }); 235 } 236 237 [[nodiscard]] constexpr bool is(HostError host_error) const { 238 return Visit([host_error](HostError held) { return held == host_error; }, 239 [](ProtocolErrorCode) { return false; }); 240 } 241 242 template <typename... Ts> 243 [[nodiscard]] constexpr bool is_any_of(Ts... error_codes) const { 244 return (is(error_codes) || ...); 245 } 246 247 // Given two "visitors" (callable objects that accept HostError and 248 // ProtocolErrorCode), invoke the one that corresponds to the error held in 249 // storage, but not the other. This pattern allows the code within the 250 // visitors to statically presume the type of the error code that they work 251 // with. Example: 252 // 253 // int ConvertToInt(Error<FooError> error) { 254 // return Visit( 255 // [](HostError held) { return static_cast<int>(held); }, 256 // [](ProtocolErrorCode held) { return static_cast<int>(held); }); 257 // ); 258 // } 259 // 260 // Unlike std::visit, the two visitors do not need to be differentiated from 261 // each other through overload resolution rules: the argument order to 262 // invoking Visit(…) is what determines which visitor gets called. 263 // 264 // Returns the return value of the visitor that was called (which may return 265 // void). 266 template <typename HostVisitor, typename ProtoVisitor> 267 [[nodiscard]] constexpr std::common_type_t< 268 std::invoke_result_t<HostVisitor, HostError>, 269 std::invoke_result_t<ProtoVisitor, ProtocolErrorCode>> 270 Visit(HostVisitor host_error_visitor, 271 ProtoVisitor proto_error_visitor) const { 272 if (is_host_error()) { 273 return host_error_visitor(host_error()); 274 } 275 return proto_error_visitor(protocol_error()); 276 } 277 278 static constexpr bool may_hold_protocol_error() { 279 return !std::is_same_v<ProtocolErrorCode, NoProtocolError>; 280 } 281 282 private: 283 // Factory functions 284 friend constexpr fit::result<Error<ProtocolErrorCode>> 285 ToResult<ProtocolErrorCode>(ProtocolErrorCode); 286 287 template <typename T = ProtocolErrorCode, 288 std::enable_if_t<detail::CanRepresentSuccessV<T>, int> = 0> 289 constexpr explicit Error(const ProtocolErrorCode& proto_error) 290 : error_(proto_error) { 291 BT_ASSERT(!ProtocolErrorTraits<ProtocolErrorCode>::is_success(proto_error)); 292 } 293 294 std::variant<HostError, ProtocolErrorCode> error_; 295 }; 296 297 // Deduction guide to allow Errors to be constructed from a HostError without 298 // specifying what protocol error the Error can hold instead. 299 Error(HostError) -> Error<NoProtocolError>; 300 301 // Comparison operators overloads useful for testing using 302 // {ASSERT,EXPECT}_{EQ,NE} GoogleTest macros. Each of these must explicitly 303 // define a operator!= as well as account for commutative calls, because C++ 304 // does not automatically generate these. Those variant overloads can not be 305 // generically defined because there's no way to test if those variants can 306 // actually be instantiated (using decltype etc), causing problems with e.g. 307 // fit::result<E, T> == fit::result<F, U>. 308 309 // Comparisons to fit::result<Error<ProtocolErrorCode>> 310 template <typename LErrorCode, typename RErrorCode, typename... Ts> 311 constexpr bool operator==(const Error<LErrorCode>& lhs, 312 const fit::result<Error<RErrorCode>, Ts...>& rhs) { 313 static_assert((!detail::IsErrorV<Ts> && ...), 314 "fit::result should not contain Error as a success value"); 315 return rhs.is_error() && (rhs.error_value() == lhs); 316 } 317 318 template <typename LErrorCode, typename RErrorCode, typename... Ts> 319 constexpr bool operator==(const fit::result<Error<LErrorCode>, Ts...>& lhs, 320 const Error<RErrorCode>& rhs) { 321 return rhs == lhs; 322 } 323 324 template <typename LErrorCode, typename RErrorCode, typename... Ts> 325 constexpr bool operator!=(const Error<LErrorCode>& lhs, 326 const fit::result<Error<RErrorCode>, Ts...>& rhs) { 327 return !(lhs == rhs); 328 } 329 330 template <typename LErrorCode, typename RErrorCode, typename... Ts> 331 constexpr bool operator!=(const fit::result<Error<LErrorCode>, Ts...>& lhs, 332 const Error<RErrorCode>& rhs) { 333 return !(rhs == lhs); 334 } 335 336 // Comparisons between fit::result<Error<…>> objects 337 // Note that this is not standard fit::result relation behavior which normally 338 // compares all error results to be equal. These are preferred in overload 339 // resolution because they are more specialized templates than the ones provided 340 // by fit. However, because they are more specialized, all of the combinations 341 // must be typed out separately to avoid ambiguous overload errors: 342 // 1. operands having zero or one success values 343 // 2. operation is == or != 344 // The case of comparing a result with a success value to a result without is 345 // intentionally not defined because it's not obvious what behavior it should 346 // have when both results hold success. 347 template <typename LErrorCode, typename RErrorCode, typename T> 348 constexpr bool operator==(const fit::result<Error<LErrorCode>, T>& lhs, 349 const fit::result<Error<RErrorCode>, T>& rhs) { 350 static_assert(!detail::IsErrorV<T>, 351 "fit::result should not contain Error as a success value"); 352 if (lhs.is_ok() != rhs.is_ok()) { 353 return false; 354 } 355 if (lhs.is_ok()) { 356 return lhs.value() == rhs.value(); 357 } 358 return lhs.error_value() == rhs.error_value(); 359 } 360 361 template <typename LErrorCode, typename RErrorCode, typename T> 362 constexpr bool operator!=(const fit::result<Error<LErrorCode>, T>& lhs, 363 const fit::result<Error<RErrorCode>, T>& rhs) { 364 return !(lhs == rhs); 365 } 366 367 template <typename LErrorCode, typename RErrorCode> 368 constexpr bool operator==(const fit::result<Error<LErrorCode>>& lhs, 369 const fit::result<Error<RErrorCode>>& rhs) { 370 if (lhs.is_ok() != rhs.is_ok()) { 371 return false; 372 } 373 if (lhs.is_ok()) { 374 return true; 375 } 376 return lhs.error_value() == rhs.error_value(); 377 } 378 379 template <typename LErrorCode, typename RErrorCode> 380 constexpr bool operator!=(const fit::result<Error<LErrorCode>>& lhs, 381 const fit::result<Error<RErrorCode>>& rhs) { 382 return !(lhs == rhs); 383 } 384 385 namespace internal { 386 387 // Helper to build a string from result using generic concatenation calls. 388 template <typename Result, typename StringBuilder, typename ValueStringBuilder> 389 void BuildResultToString(const Result& result, 390 StringBuilder builder, 391 ValueStringBuilder append_value) { 392 builder("[result: "); 393 if (result.is_ok()) { 394 builder("ok("); 395 append_value(); 396 } else { 397 builder("error("); 398 builder(result.error_value().ToString()); 399 } 400 builder(")]"); 401 } 402 403 // Produces a human-readable representation of a fit::result<Error<…>> 404 template <typename ProtocolErrorCode, typename... Ts> 405 std::string ToString( 406 const fit::result<Error<ProtocolErrorCode>, Ts...>& result) { 407 std::string out; 408 auto append_value_string = [&] { 409 if constexpr (sizeof...(Ts) > 0) { 410 if constexpr ((bt::internal::HasToStringV<Ts> && ...)) { 411 out += ToString(result.value()); 412 } else { 413 // It's not possible to portably print e.g. the name of the value's 414 // type, so fall back to a placeholder. It may be useful to print the 415 // size and a hexdump, however. 416 out += "?"; 417 } 418 } 419 }; 420 bt::internal::BuildResultToString( 421 result, [&](auto s) { out += s; }, append_value_string); 422 return out; 423 } 424 425 } // namespace internal 426 427 namespace detail { 428 429 // Contains a |value| bool member that is true if overload operator<<(Lhs&, 430 // const Rhs&) exists 431 template <typename Lhs, typename Rhs, typename = void> 432 struct IsStreamable : std::false_type {}; 433 434 template <typename Lhs, typename Rhs> 435 struct IsStreamable< 436 Lhs, 437 Rhs, 438 std::void_t<decltype(std::declval<Lhs&>() << std::declval<const Rhs&>())>> 439 : std::is_same<Lhs&, 440 decltype(std::declval<Lhs&>() 441 << std::declval<const Rhs&>())> {}; 442 443 template <typename Lhs, typename Rhs> 444 constexpr bool IsStreamableV = IsStreamable<Lhs, Rhs>::value; 445 446 } // namespace detail 447 } // namespace bt 448 449 // Extends the GoogleTest value printer to print fit::result<bt::Error<…>, …> 450 // types, including converting the contained value of "success" results to 451 // strings when possible. 452 // 453 // This must be defined in namespace fit because GoogleTest uses 454 // argument-dependent lookup (ADL) to find this overload. |os|'s type is 455 // templated in order to avoid including <iostream>. 456 namespace fit { 457 458 // Some GoogleTest internal objects (like testing::Message, the return type of 459 // ADD_FAILURE()) declare broad operator<< overloads that conflict with this 460 // one. In those cases, it's likely easiest to wrap the result in bt_str(…). 461 template <typename OStream, typename ProtocolErrorCode, typename... Ts> 462 OStream& operator<<( 463 OStream& os, 464 const fit::result<::bt::Error<ProtocolErrorCode>, Ts...>& result) { 465 auto stream_value_string = [&] { 466 if constexpr (sizeof...(Ts) > 0) { 467 if constexpr ((::bt::internal::HasToStringV<Ts> && ...)) { 468 os << ::bt::internal::ToString(result.value()); 469 } else if constexpr ((::bt::detail::IsStreamableV<OStream, Ts> && ...)) { 470 os << result.value(); 471 } else { 472 // It may be prettier to default to ::testing::PrintToString here but 473 // that would require including <gtest/gtest.h> here, which is not 474 // ideal. 475 os << "?"; 476 } 477 } 478 }; 479 ::bt::internal::BuildResultToString( 480 result, [&](auto s) { os << s; }, stream_value_string); 481 return os; 482 } 483 484 } // namespace fit 485 486 // Macro to check and log any non-Success status of an event. 487 // Use these like: 488 // if (bt_is_error(result, WARN, "gap", "failed to set event mask")) { 489 // ... 490 // return; 491 // } 492 // 493 // It will log with the string prepended to the stringified result if result is 494 // a failure. Evaluates to true if the result indicates failure. 495 #define bt_is_error(result, level, tag, fmt, args...) \ 496 ({ \ 497 auto _result = result; \ 498 if (_result.is_error()) \ 499 bt_log(level, tag, "%s: " fmt, bt_str(_result), ##args); \ 500 _result.is_error(); \ 501 }) 502