boost_error_code.hpp 11 KB

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