status_code.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /* Proposed SG14 status_code
  2. (C) 2018-2019 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
  3. File Created: Feb 2018
  4. Boost Software License - Version 1.0 - August 17th, 2003
  5. Permission is hereby granted, free of charge, to any person or organization
  6. obtaining a copy of the software and accompanying documentation covered by
  7. this license (the "Software") to use, reproduce, display, distribute,
  8. execute, and transmit the Software, and to prepare derivative works of the
  9. Software, and to permit third-parties to whom the Software is furnished to
  10. do so, all subject to the following:
  11. The copyright notices in the Software and this entire statement, including
  12. the above license grant, this restriction and the following disclaimer,
  13. must be included in all copies of the Software, in whole or in part, and
  14. all derivative works of the Software, unless such copies or derivative
  15. works are solely in the form of machine-executable object code generated by
  16. a source language processor.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  20. SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  21. FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  22. ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. DEALINGS IN THE SOFTWARE.
  24. */
  25. #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
  26. #define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
  27. #include "status_code_domain.hpp"
  28. #if __cplusplus >= 201700 || _HAS_CXX17
  29. // 0.26
  30. #include <utility> // for in_place
  31. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
  32. using in_place_t = std::in_place_t;
  33. using std::in_place;
  34. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
  35. #else
  36. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
  37. //! Aliases `std::in_place_t` if on C++ 17 or later, else defined locally.
  38. struct in_place_t
  39. {
  40. explicit in_place_t() = default;
  41. };
  42. //! Aliases `std::in_place` if on C++ 17 or later, else defined locally.
  43. constexpr in_place_t in_place{};
  44. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
  45. #endif
  46. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
  47. //! Namespace for user injected mixins
  48. namespace mixins
  49. {
  50. template <class Base, class T> struct mixin : public Base
  51. {
  52. using Base::Base;
  53. };
  54. } // namespace mixins
  55. /*! A tag for an erased value type for `status_code<D>`.
  56. Available only if `ErasedType` satisfies `traits::is_move_relocating<ErasedType>::value`.
  57. */
  58. template <class ErasedType, //
  59. typename std::enable_if<traits::is_move_relocating<ErasedType>::value, bool>::type = true>
  60. struct erased
  61. {
  62. using value_type = ErasedType;
  63. };
  64. namespace detail
  65. {
  66. template <class T> struct is_status_code
  67. {
  68. static constexpr bool value = false;
  69. };
  70. template <class T> struct is_status_code<status_code<T>>
  71. {
  72. static constexpr bool value = true;
  73. };
  74. template <class T> struct is_erased_status_code
  75. {
  76. static constexpr bool value = false;
  77. };
  78. template <class T> struct is_erased_status_code<status_code<erased<T>>>
  79. {
  80. static constexpr bool value = true;
  81. };
  82. // From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf
  83. namespace impl
  84. {
  85. template <typename... Ts> struct make_void
  86. {
  87. using type = void;
  88. };
  89. template <typename... Ts> using void_t = typename make_void<Ts...>::type;
  90. template <class...> struct types
  91. {
  92. using type = types;
  93. };
  94. template <template <class...> class T, class types, class = void> struct test_apply
  95. {
  96. using type = void;
  97. };
  98. template <template <class...> class T, class... Ts> struct test_apply<T, types<Ts...>, void_t<T<Ts...>>>
  99. {
  100. using type = T<Ts...>;
  101. };
  102. } // namespace impl
  103. template <template <class...> class T, class... Ts> using test_apply = impl::test_apply<T, impl::types<Ts...>>;
  104. template <class T, class... Args> using get_make_status_code_result = decltype(make_status_code(std::declval<T>(), std::declval<Args>()...));
  105. template <class... Args> using safe_get_make_status_code_result = test_apply<get_make_status_code_result, Args...>;
  106. } // namespace detail
  107. //! Trait returning true if the type is a status code.
  108. template <class T> struct is_status_code
  109. {
  110. static constexpr bool value = detail::is_status_code<typename std::decay<T>::type>::value || detail::is_erased_status_code<typename std::decay<T>::type>::value;
  111. };
  112. /*! A type erased lightweight status code reflecting empty, success, or failure.
  113. Differs from `status_code<erased<>>` by being always available irrespective of
  114. the domain's value type, but cannot be copied, moved, nor destructed. Thus one
  115. always passes this around by const lvalue reference.
  116. */
  117. template <> class status_code<void>
  118. {
  119. template <class T> friend class status_code;
  120. public:
  121. //! The type of the domain.
  122. using domain_type = void;
  123. //! The type of the status code.
  124. using value_type = void;
  125. //! The type of a reference to a message string.
  126. using string_ref = typename status_code_domain::string_ref;
  127. protected:
  128. const status_code_domain *_domain{nullptr};
  129. protected:
  130. //! No default construction at type erased level
  131. status_code() = default;
  132. //! No public copying at type erased level
  133. status_code(const status_code &) = default;
  134. //! No public moving at type erased level
  135. status_code(status_code &&) = default;
  136. //! No public assignment at type erased level
  137. status_code &operator=(const status_code &) = default;
  138. //! No public assignment at type erased level
  139. status_code &operator=(status_code &&) = default;
  140. //! No public destruction at type erased level
  141. ~status_code() = default;
  142. //! Used to construct a non-empty type erased status code
  143. constexpr explicit status_code(const status_code_domain *v) noexcept : _domain(v) {}
  144. public:
  145. //! Return the status code domain.
  146. constexpr const status_code_domain &domain() const noexcept { return *_domain; }
  147. //! True if the status code is empty.
  148. BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _domain == nullptr; }
  149. //! Return a reference to a string textually representing a code.
  150. string_ref message() const noexcept { return (_domain != nullptr) ? _domain->_do_message(*this) : string_ref("(empty)"); }
  151. //! True if code means success.
  152. bool success() const noexcept { return (_domain != nullptr) ? !_domain->_do_failure(*this) : false; }
  153. //! True if code means failure.
  154. bool failure() const noexcept { return (_domain != nullptr) ? _domain->_do_failure(*this) : false; }
  155. /*! True if code is strictly (and potentially non-transitively) semantically equivalent to another code in another domain.
  156. Note that usually non-semantic i.e. pure value comparison is used when the other status code has the same domain.
  157. As `equivalent()` will try mapping to generic code, this usually captures when two codes have the same semantic
  158. meaning in `equivalent()`.
  159. */
  160. template <class T> bool strictly_equivalent(const status_code<T> &o) const noexcept
  161. {
  162. if(_domain && o._domain)
  163. {
  164. return _domain->_do_equivalent(*this, o);
  165. }
  166. // If we are both empty, we are equivalent
  167. if(!_domain && !o._domain)
  168. {
  169. return true; // NOLINT
  170. }
  171. // Otherwise not equivalent
  172. return false;
  173. }
  174. /*! True if code is equivalent, by any means, to another code in another domain (guaranteed transitive).
  175. Firstly `strictly_equivalent()` is run in both directions. If neither succeeds, each domain is asked
  176. for the equivalent generic code and those are compared.
  177. */
  178. template <class T> inline bool equivalent(const status_code<T> &o) const noexcept;
  179. #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
  180. //! Throw a code as a C++ exception.
  181. BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN void throw_exception() const { _domain->_do_throw_exception(*this); }
  182. #endif
  183. };
  184. namespace detail
  185. {
  186. template <class DomainType> struct get_domain_value_type
  187. {
  188. using domain_type = DomainType;
  189. using value_type = typename domain_type::value_type;
  190. };
  191. template <class ErasedType> struct get_domain_value_type<erased<ErasedType>>
  192. {
  193. using domain_type = status_code_domain;
  194. using value_type = ErasedType;
  195. };
  196. template <class DomainType> class status_code_storage : public status_code<void>
  197. {
  198. using _base = status_code<void>;
  199. public:
  200. //! The type of the domain.
  201. using domain_type = typename get_domain_value_type<DomainType>::domain_type;
  202. //! The type of the status code.
  203. using value_type = typename get_domain_value_type<DomainType>::value_type;
  204. //! The type of a reference to a message string.
  205. using string_ref = typename domain_type::string_ref;
  206. #ifndef NDEBUG
  207. static_assert(std::is_move_constructible<value_type>::value || std::is_copy_constructible<value_type>::value, "DomainType::value_type is neither move nor copy constructible!");
  208. static_assert(!std::is_default_constructible<value_type>::value || std::is_nothrow_default_constructible<value_type>::value, "DomainType::value_type is not nothrow default constructible!");
  209. static_assert(!std::is_move_constructible<value_type>::value || std::is_nothrow_move_constructible<value_type>::value, "DomainType::value_type is not nothrow move constructible!");
  210. static_assert(std::is_nothrow_destructible<value_type>::value, "DomainType::value_type is not nothrow destructible!");
  211. #endif
  212. // Replace the type erased implementations with type aware implementations for better codegen
  213. //! Return the status code domain.
  214. constexpr const domain_type &domain() const noexcept { return *static_cast<const domain_type *>(this->_domain); }
  215. //! Reset the code to empty.
  216. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept
  217. {
  218. this->_value.~value_type();
  219. this->_domain = nullptr;
  220. new(&this->_value) value_type();
  221. }
  222. #if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
  223. //! Return a reference to the `value_type`.
  224. constexpr value_type &value() & noexcept { return this->_value; }
  225. //! Return a reference to the `value_type`.
  226. constexpr value_type &&value() && noexcept { return this->_value; }
  227. #endif
  228. //! Return a reference to the `value_type`.
  229. constexpr const value_type &value() const &noexcept { return this->_value; }
  230. //! Return a reference to the `value_type`.
  231. constexpr const value_type &&value() const &&noexcept { return this->_value; }
  232. protected:
  233. status_code_storage() = default;
  234. status_code_storage(const status_code_storage &) = default;
  235. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage(status_code_storage &&o) noexcept : _base(static_cast<status_code_storage &&>(o)), _value(static_cast<status_code_storage &&>(o)._value) { o._domain = nullptr; }
  236. status_code_storage &operator=(const status_code_storage &) = default;
  237. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage &operator=(status_code_storage &&o) noexcept
  238. {
  239. this->~status_code_storage();
  240. new(this) status_code_storage(static_cast<status_code_storage &&>(o));
  241. return *this;
  242. }
  243. ~status_code_storage() = default;
  244. value_type _value{};
  245. struct _value_type_constructor
  246. {
  247. };
  248. template <class... Args>
  249. constexpr status_code_storage(_value_type_constructor /*unused*/, const status_code_domain *v, Args &&... args)
  250. : _base(v)
  251. , _value(static_cast<Args &&>(args)...)
  252. {
  253. }
  254. };
  255. } // namespace detail
  256. /*! A lightweight, typed, status code reflecting empty, success, or failure.
  257. This is the main workhorse of the system_error2 library. Its characteristics reflect the value type
  258. set by its domain type, so if that value type is move-only or trivial, so is this.
  259. An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
  260. If it is found, and it generates a status code compatible with this status code, implicit construction
  261. is made available.
  262. You may mix in custom member functions and member function overrides by injecting a specialisation of
  263. `mixins::mixin<Base, YourDomainType>`. Your mixin must inherit from `Base`.
  264. */
  265. template <class DomainType> class status_code : public mixins::mixin<detail::status_code_storage<DomainType>, DomainType>
  266. {
  267. template <class T> friend class status_code;
  268. using _base = mixins::mixin<detail::status_code_storage<DomainType>, DomainType>;
  269. public:
  270. //! The type of the domain.
  271. using domain_type = DomainType;
  272. //! The type of the status code.
  273. using value_type = typename domain_type::value_type;
  274. //! The type of a reference to a message string.
  275. using string_ref = typename domain_type::string_ref;
  276. public:
  277. //! Default construction to empty
  278. status_code() = default;
  279. //! Copy constructor
  280. status_code(const status_code &) = default;
  281. //! Move constructor
  282. status_code(status_code &&) = default; // NOLINT
  283. //! Copy assignment
  284. status_code &operator=(const status_code &) = default;
  285. //! Move assignment
  286. status_code &operator=(status_code &&) = default; // NOLINT
  287. ~status_code() = default;
  288. //! Return a copy of the code.
  289. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code clone() const { return *this; }
  290. /***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
  291. //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
  292. template <class T, class... Args, //
  293. class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<T, Args...>::type, // Safe ADL lookup of make_status_code(), returns void if not found
  294. typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code>::value // not copy/move of self
  295. && !std::is_same<typename std::decay<T>::type, in_place_t>::value // not in_place_t
  296. && is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
  297. && std::is_constructible<status_code, MakeStatusCodeResult>::value, // ADLed status code is compatible
  298. bool>::type = true>
  299. constexpr status_code(T &&v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
  300. : status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
  301. {
  302. }
  303. //! Explicit in-place construction.
  304. template <class... Args>
  305. constexpr explicit status_code(in_place_t /*unused */, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args &&...>::value)
  306. : _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<Args &&>(args)...)
  307. {
  308. }
  309. //! Explicit in-place construction from initialiser list.
  310. template <class T, class... Args>
  311. constexpr explicit status_code(in_place_t /*unused */, std::initializer_list<T> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args &&...>::value)
  312. : _base(typename _base::_value_type_constructor{}, &domain_type::get(), il, static_cast<Args &&>(args)...)
  313. {
  314. }
  315. //! Explicit copy construction from a `value_type`.
  316. constexpr explicit status_code(const value_type &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
  317. : _base(typename _base::_value_type_constructor{}, &domain_type::get(), v)
  318. {
  319. }
  320. //! Explicit move construction from a `value_type`.
  321. constexpr explicit status_code(value_type &&v) noexcept(std::is_nothrow_move_constructible<value_type>::value)
  322. : _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<value_type &&>(v))
  323. {
  324. }
  325. /*! Explicit construction from an erased status code. Available only if
  326. `value_type` is trivially copyable or move relocating, and `sizeof(status_code) <= sizeof(status_code<erased<>>)`.
  327. Does not check if domains are equal.
  328. */
  329. template <class ErasedType, //
  330. typename std::enable_if<detail::type_erasure_is_safe<ErasedType, value_type>::value, bool>::type = true>
  331. constexpr explicit status_code(const status_code<erased<ErasedType>> &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
  332. : status_code(detail::erasure_cast<value_type>(v.value()))
  333. {
  334. #if __cplusplus >= 201400
  335. assert(v.domain() == this->domain());
  336. #endif
  337. }
  338. //! Assignment from a `value_type`.
  339. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code &operator=(const value_type &v) noexcept(std::is_nothrow_copy_assignable<value_type>::value)
  340. {
  341. this->_value = v;
  342. return *this;
  343. }
  344. //! Return a reference to a string textually representing a code.
  345. string_ref message() const noexcept { return this->_domain ? string_ref(this->domain()._do_message(*this)) : string_ref("(empty)"); }
  346. };
  347. namespace traits
  348. {
  349. template <class DomainType> struct is_move_relocating<status_code<DomainType>>
  350. {
  351. static constexpr bool value = is_move_relocating<typename DomainType::value_type>::value;
  352. };
  353. } // namespace traits
  354. /*! Type erased, move-only status_code, unlike `status_code<void>` which cannot be moved nor destroyed. Available
  355. only if `erased<>` is available, which is when the domain's type is trivially
  356. copyable or is move relocatable, and if the size of the domain's typed error code is less than or equal to
  357. this erased error code. Copy construction is disabled, but if you want a copy call `.clone()`.
  358. An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
  359. If it is found, and it generates a status code compatible with this status code, implicit construction
  360. is made available.
  361. */
  362. template <class ErasedType> class status_code<erased<ErasedType>> : public mixins::mixin<detail::status_code_storage<erased<ErasedType>>, erased<ErasedType>>
  363. {
  364. template <class T> friend class status_code;
  365. using _base = mixins::mixin<detail::status_code_storage<erased<ErasedType>>, erased<ErasedType>>;
  366. public:
  367. //! The type of the domain (void, as it is erased).
  368. using domain_type = void;
  369. //! The type of the erased status code.
  370. using value_type = ErasedType;
  371. //! The type of a reference to a message string.
  372. using string_ref = typename _base::string_ref;
  373. public:
  374. //! Default construction to empty
  375. status_code() = default;
  376. //! Copy constructor
  377. status_code(const status_code &) = delete;
  378. //! Move constructor
  379. status_code(status_code &&) = default; // NOLINT
  380. //! Copy assignment
  381. status_code &operator=(const status_code &) = delete;
  382. //! Move assignment
  383. status_code &operator=(status_code &&) = default; // NOLINT
  384. ~status_code()
  385. {
  386. if(nullptr != this->_domain)
  387. {
  388. this->_domain->_do_erased_destroy(*this, sizeof(*this));
  389. }
  390. }
  391. //! Return a copy of the erased code by asking the domain to perform the erased copy.
  392. status_code clone() const
  393. {
  394. if(nullptr == this->_domain)
  395. {
  396. return {};
  397. }
  398. status_code x;
  399. this->_domain->_do_erased_copy(x, *this, sizeof(*this));
  400. return x;
  401. }
  402. /***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
  403. //! Implicit copy construction from any other status code if its value type is trivially copyable and it would fit into our storage
  404. template <class DomainType, //
  405. typename std::enable_if<!detail::is_erased_status_code<status_code<DomainType>>::value //
  406. && std::is_trivially_copyable<typename DomainType::value_type>::value //
  407. && detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
  408. bool>::type = true>
  409. constexpr status_code(const status_code<DomainType> &v) noexcept // NOLINT
  410. : _base(typename _base::_value_type_constructor{}, &v.domain(), detail::erasure_cast<value_type>(v.value()))
  411. {
  412. }
  413. //! Implicit move construction from any other status code if its value type is trivially copyable or move relocating and it would fit into our storage
  414. template <class DomainType, //
  415. typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value, bool>::type = true>
  416. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(status_code<DomainType> &&v) noexcept // NOLINT
  417. : _base(typename _base::_value_type_constructor{}, &v.domain(), detail::erasure_cast<value_type>(v.value()))
  418. {
  419. v._domain = nullptr;
  420. }
  421. //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
  422. template <class T, class... Args, //
  423. class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<T, Args...>::type, // Safe ADL lookup of make_status_code(), returns void if not found
  424. typename std::enable_if<!std::is_same<typename std::decay<T>::type, status_code>::value // not copy/move of self
  425. && !std::is_same<typename std::decay<T>::type, value_type>::value // not copy/move of value type
  426. && is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
  427. && std::is_constructible<status_code, MakeStatusCodeResult>::value, // ADLed status code is compatible
  428. bool>::type = true>
  429. constexpr status_code(T &&v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
  430. : status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
  431. {
  432. }
  433. /**** By rights ought to be removed in any formal standard ****/
  434. //! Reset the code to empty.
  435. BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept { *this = status_code(); }
  436. //! Return the erased `value_type` by value.
  437. constexpr value_type value() const noexcept { return this->_value; }
  438. };
  439. namespace traits
  440. {
  441. template <class ErasedType> struct is_move_relocating<status_code<erased<ErasedType>>>
  442. {
  443. static constexpr bool value = true;
  444. };
  445. } // namespace traits
  446. BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
  447. #endif