std_error_code.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /* Proposed SG14 status_code
  2. (C) 2018 - 2021 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
  3. File Created: Aug 2018
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License in the accompanying file
  7. Licence.txt or at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. Distributed under the Boost Software License, Version 1.0.
  15. (See accompanying file Licence.txt or copy at
  16. http://www.boost.org/LICENSE_1_0.txt)
  17. */
  18. #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STD_ERROR_CODE_HPP
  19. #define BOOST_OUTCOME_SYSTEM_ERROR2_STD_ERROR_CODE_HPP
  20. #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
  21. #include "posix_code.hpp"
  22. #endif
  23. #if defined(_WIN32) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
  24. #include "win32_code.hpp"
  25. #endif
  26. #include <system_error>
  27. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
  28. class _std_error_code_domain;
  29. //! A `status_code` representing exactly a `std::error_code`
  30. using std_error_code = status_code<_std_error_code_domain>;
  31. namespace mixins
  32. {
  33. template <class Base> struct mixin<Base, _std_error_code_domain> : public Base
  34. {
  35. using Base::Base;
  36. //! Implicit constructor from a `std::error_code`
  37. inline mixin(std::error_code ec);
  38. //! Returns the error code category
  39. inline const std::error_category &category() const noexcept;
  40. };
  41. } // namespace mixins
  42. /*! The implementation of the domain for `std::error_code` error codes.
  43. */
  44. class _std_error_code_domain final : public status_code_domain
  45. {
  46. template <class DomainType> friend class status_code;
  47. using _base = status_code_domain;
  48. using _error_code_type = std::error_code;
  49. using _error_category_type = std::error_category;
  50. std::string _name;
  51. static _base::string_ref _make_string_ref(_error_code_type c) noexcept
  52. {
  53. #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
  54. try
  55. #endif
  56. {
  57. std::string msg = c.message();
  58. auto *p = static_cast<char *>(malloc(msg.size() + 1)); // NOLINT
  59. if(p == nullptr)
  60. {
  61. return _base::string_ref("failed to allocate message");
  62. }
  63. memcpy(p, msg.c_str(), msg.size() + 1);
  64. return _base::atomic_refcounted_string_ref(p, msg.size());
  65. }
  66. #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
  67. catch(...)
  68. {
  69. return _base::string_ref("failed to allocate message");
  70. }
  71. #endif
  72. }
  73. public:
  74. //! The value type of the `std::error_code` code, which stores the `int` from the `std::error_code`
  75. using value_type = int;
  76. using _base::string_ref;
  77. //! Returns the error category singleton pointer this status code domain represents
  78. const _error_category_type &error_category() const noexcept
  79. {
  80. auto ptr = 0x223a160d20de97b4 ^ this->id();
  81. return *reinterpret_cast<const _error_category_type *>(ptr);
  82. }
  83. //! Default constructor
  84. explicit _std_error_code_domain(const _error_category_type &category) noexcept
  85. : _base(0x223a160d20de97b4 ^ reinterpret_cast<_base::unique_id_type>(&category))
  86. , _name("std_error_code_domain(")
  87. {
  88. _name.append(category.name());
  89. _name.push_back(')');
  90. }
  91. _std_error_code_domain(const _std_error_code_domain &) = default;
  92. _std_error_code_domain(_std_error_code_domain &&) = default;
  93. _std_error_code_domain &operator=(const _std_error_code_domain &) = default;
  94. _std_error_code_domain &operator=(_std_error_code_domain &&) = default;
  95. ~_std_error_code_domain() = default;
  96. static inline const _std_error_code_domain *get(_error_code_type ec);
  97. virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT
  98. virtual payload_info_t payload_info() const noexcept override
  99. {
  100. return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
  101. (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
  102. }
  103. protected:
  104. virtual bool _do_failure(const status_code<void> &code) const noexcept override;
  105. virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override;
  106. virtual generic_code _generic_code(const status_code<void> &code) const noexcept override;
  107. virtual string_ref _do_message(const status_code<void> &code) const noexcept override;
  108. #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
  109. BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override;
  110. #endif
  111. };
  112. namespace detail
  113. {
  114. extern inline _std_error_code_domain *std_error_code_domain_from_category(const std::error_category &category)
  115. {
  116. static constexpr size_t max_items = 64;
  117. static struct storage_t
  118. {
  119. std::atomic<unsigned> _lock;
  120. union item_t
  121. {
  122. int _init;
  123. _std_error_code_domain domain;
  124. constexpr item_t()
  125. : _init(0)
  126. {
  127. }
  128. ~item_t() {}
  129. } items[max_items];
  130. size_t count{0};
  131. void lock()
  132. {
  133. while(_lock.exchange(1, std::memory_order_acquire) != 0)
  134. ;
  135. }
  136. void unlock() { _lock.store(0, std::memory_order_release); }
  137. storage_t() {}
  138. ~storage_t()
  139. {
  140. lock();
  141. for(size_t n = 0; n < count; n++)
  142. {
  143. items[n].domain.~_std_error_code_domain();
  144. }
  145. unlock();
  146. }
  147. _std_error_code_domain *add(const std::error_category &category)
  148. {
  149. _std_error_code_domain *ret = nullptr;
  150. lock();
  151. for(size_t n = 0; n < count; n++)
  152. {
  153. if(items[n].domain.error_category() == category)
  154. {
  155. ret = &items[n].domain;
  156. break;
  157. }
  158. }
  159. if(ret == nullptr && count < max_items)
  160. {
  161. ret = new(std::addressof(items[count++].domain)) _std_error_code_domain(category);
  162. }
  163. unlock();
  164. return ret;
  165. }
  166. } storage;
  167. return storage.add(category);
  168. }
  169. } // namespace detail
  170. namespace mixins
  171. {
  172. template <class Base>
  173. inline mixin<Base, _std_error_code_domain>::mixin(std::error_code ec)
  174. : Base(typename Base::_value_type_constructor{}, _std_error_code_domain::get(ec), ec.value())
  175. {
  176. }
  177. template <class Base> inline const std::error_category &mixin<Base, _std_error_code_domain>::category() const noexcept
  178. {
  179. const auto &domain = static_cast<const _std_error_code_domain &>(this->domain());
  180. return domain.error_category();
  181. };
  182. } // namespace mixins
  183. inline const _std_error_code_domain *_std_error_code_domain::get(std::error_code ec)
  184. {
  185. auto *p = detail::std_error_code_domain_from_category(ec.category());
  186. assert(p != nullptr);
  187. if(p == nullptr)
  188. {
  189. abort();
  190. }
  191. return p;
  192. }
  193. inline bool _std_error_code_domain::_do_failure(const status_code<void> &code) const noexcept
  194. {
  195. assert(code.domain() == *this);
  196. return static_cast<const std_error_code &>(code).value() != 0; // NOLINT
  197. }
  198. inline bool _std_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept
  199. {
  200. assert(code1.domain() == *this);
  201. const auto &c1 = static_cast<const std_error_code &>(code1); // NOLINT
  202. const auto &cat1 = c1.category();
  203. // Are we comparing to another wrapped error_code?
  204. if(code2.domain() == *this)
  205. {
  206. const auto &c2 = static_cast<const std_error_code &>(code2); // NOLINT
  207. const auto &cat2 = c2.category();
  208. // If the error code categories are identical, do literal comparison
  209. if(cat1 == cat2)
  210. {
  211. return c1.value() == c2.value();
  212. }
  213. // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition()
  214. return false;
  215. }
  216. // Am I an error code with generic category?
  217. if(cat1 == std::generic_category())
  218. {
  219. // Convert to generic code, and compare that
  220. generic_code _c1(static_cast<errc>(c1.value()));
  221. return _c1 == code2;
  222. }
  223. // Am I an error code with system category?
  224. if(cat1 == std::system_category())
  225. {
  226. // Convert to POSIX or Win32 code, and compare that
  227. #ifdef _WIN32
  228. win32_code _c1((win32::DWORD) c1.value());
  229. return _c1 == code2;
  230. #elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX)
  231. posix_code _c1(c1.value());
  232. return _c1 == code2;
  233. #endif
  234. }
  235. return false;
  236. }
  237. inline generic_code _std_error_code_domain::_generic_code(const status_code<void> &code) const noexcept
  238. {
  239. assert(code.domain() == *this);
  240. const auto &c = static_cast<const std_error_code &>(code); // NOLINT
  241. // Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc.
  242. std::error_condition cond(c.category().default_error_condition(c.value()));
  243. if(cond.category() == std::generic_category())
  244. {
  245. return generic_code(static_cast<errc>(cond.value()));
  246. }
  247. #if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32)
  248. if(cond.category() == std::system_category())
  249. {
  250. return generic_code(static_cast<errc>(cond.value()));
  251. }
  252. #endif
  253. return errc::unknown;
  254. }
  255. inline _std_error_code_domain::string_ref _std_error_code_domain::_do_message(const status_code<void> &code) const noexcept
  256. {
  257. assert(code.domain() == *this);
  258. const auto &c = static_cast<const std_error_code &>(code); // NOLINT
  259. return _make_string_ref(_error_code_type(c.value(), c.category()));
  260. }
  261. #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
  262. BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN inline void _std_error_code_domain::_do_throw_exception(const status_code<void> &code) const
  263. {
  264. assert(code.domain() == *this);
  265. const auto &c = static_cast<const std_error_code &>(code); // NOLINT
  266. throw std::system_error(std::error_code(c.value(), c.category()));
  267. }
  268. #endif
  269. static_assert(sizeof(std_error_code) <= sizeof(void *) * 2, "std_error_code does not fit into a system_code!");
  270. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
  271. // Enable implicit construction of `std_error_code` from `std::error_code`.
  272. namespace std
  273. {
  274. inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept
  275. {
  276. return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code(c);
  277. }
  278. } // namespace std
  279. #endif