status.hpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*************************************************************************
  2. *
  3. * Copyright 2021 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * 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, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #pragma once
  19. #include <atomic>
  20. #include <cstdint>
  21. #include <iosfwd>
  22. #include <string>
  23. #include "realm/error_codes.hpp"
  24. #include "realm/util/bind_ptr.hpp"
  25. #include "realm/util/features.h"
  26. namespace realm {
  27. class REALM_NODISCARD Status {
  28. public:
  29. /*
  30. * This is the best way to construct a Status that represents a non-error condition.
  31. */
  32. static inline Status OK();
  33. /*
  34. * You can construct a Status from anything that can construct a std::string_view.
  35. */
  36. template <typename Reason, std::enable_if_t<std::is_constructible_v<std::string_view, Reason>, int> = 0>
  37. Status(ErrorCodes::Error code, Reason&& reason)
  38. : m_error(ErrorInfo::create(code, std::string{std::string_view{reason}}))
  39. {
  40. }
  41. Status(ErrorCodes::Error code, std::string&& reason)
  42. : m_error(ErrorInfo::create(code, std::move(reason)))
  43. {
  44. }
  45. /*
  46. * Copying a Status is just copying an intrusive pointer - i.e. very cheap. Moving them is similarly cheap.
  47. */
  48. inline Status(const Status& other);
  49. inline Status& operator=(const Status& other);
  50. inline Status(Status&& other) noexcept;
  51. inline Status& operator=(Status&& other) noexcept;
  52. inline bool is_ok() const noexcept;
  53. inline const std::string& reason() const noexcept;
  54. inline ErrorCodes::Error code() const noexcept;
  55. inline std::string_view code_string() const noexcept;
  56. /*
  57. * This class is marked nodiscard so that we always handle errors. If there is a place where we need
  58. * to explicitly ignore an error, you can call this function, which does nothing, to satisfy the compiler.
  59. */
  60. void ignore() const noexcept {}
  61. private:
  62. Status() = default;
  63. struct ErrorInfo {
  64. mutable std::atomic<uint32_t> m_refs;
  65. const ErrorCodes::Error m_code;
  66. const std::string m_reason;
  67. static util::bind_ptr<ErrorInfo> create(ErrorCodes::Error code, std::string&& reason);
  68. protected:
  69. template <typename>
  70. friend class ::realm::util::bind_ptr;
  71. inline void bind_ptr() const noexcept
  72. {
  73. m_refs.fetch_add(1, std::memory_order_relaxed);
  74. }
  75. inline void unbind_ptr() const noexcept
  76. {
  77. if (m_refs.fetch_sub(1, std::memory_order_acq_rel) == 1) {
  78. delete this;
  79. }
  80. }
  81. private:
  82. ErrorInfo(ErrorCodes::Error code, std::string&& reason);
  83. };
  84. util::bind_ptr<ErrorInfo> m_error = {};
  85. };
  86. std::ostream& operator<<(std::ostream& out, const Status& val);
  87. inline bool operator==(const Status& lhs, const Status& rhs) noexcept
  88. {
  89. return lhs.code() == rhs.code();
  90. }
  91. inline bool operator!=(const Status& lhs, const Status& rhs) noexcept
  92. {
  93. return lhs.code() != rhs.code();
  94. }
  95. inline bool operator==(const Status& lhs, ErrorCodes::Error rhs) noexcept
  96. {
  97. return lhs.code() == rhs;
  98. }
  99. inline bool operator!=(const Status& lhs, ErrorCodes::Error rhs) noexcept
  100. {
  101. return lhs.code() != rhs;
  102. }
  103. inline Status Status::OK()
  104. {
  105. // Returns a status with m_error set to nullptr.
  106. return Status{};
  107. }
  108. inline Status::Status(const Status& other)
  109. : m_error(other.m_error)
  110. {
  111. }
  112. inline Status& Status::operator=(const Status& other)
  113. {
  114. m_error = other.m_error;
  115. return *this;
  116. }
  117. inline Status::Status(Status&& other) noexcept
  118. : m_error(std::move(other.m_error))
  119. {
  120. }
  121. inline Status& Status::operator=(Status&& other) noexcept
  122. {
  123. m_error = std::move(other.m_error);
  124. return *this;
  125. }
  126. inline bool Status::is_ok() const noexcept
  127. {
  128. return !m_error;
  129. }
  130. inline const std::string& Status::reason() const noexcept
  131. {
  132. static const std::string empty;
  133. return m_error ? m_error->m_reason : empty;
  134. }
  135. inline ErrorCodes::Error Status::code() const noexcept
  136. {
  137. return m_error ? m_error->m_code : ErrorCodes::OK;
  138. }
  139. inline std::string_view Status::code_string() const noexcept
  140. {
  141. return ErrorCodes::error_string(code());
  142. }
  143. } // namespace realm