1 /* Proposed SG14 status_code
2 (C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
6 Boost Software License - Version 1.0 - August 17th, 2003
8 Permission is hereby granted, free of charge, to any person or organization
9 obtaining a copy of the software and accompanying documentation covered by
10 this license (the "Software") to use, reproduce, display, distribute,
11 execute, and transmit the Software, and to prepare derivative works of the
12 Software, and to permit third-parties to whom the Software is furnished to
13 do so, all subject to the following:
15 The copyright notices in the Software and this entire statement, including
16 the above license grant, this restriction and the following disclaimer,
17 must be included in all copies of the Software, in whole or in part, and
18 all derivative works of the Software, unless such copies or derivative
19 works are solely in the form of machine-executable object code generated by
20 a source language processor.
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 DEALINGS IN THE SOFTWARE.
31 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
32 #define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
36 #include <cstring> // for strchr
38 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
40 /*! The main workhorse of the system_error2 library, can be typed (`status_code<DomainType>`), erased-immutable (`status_code<void>`) or erased-mutable (`status_code<erased<T>>`).
42 Be careful of placing these into containers! Equality and inequality operators are
43 *semantic* not exact. Therefore two distinct items will test true! To help prevent
44 surprise on this, `operator<` and `std::hash<>` are NOT implemented in order to
45 trap potential incorrectness. Define your own custom comparison functions for your
46 container which perform exact comparisons.
48 template <class DomainType> class status_code;
49 class _generic_code_domain;
50 //! The generic code is a status code with the generic code domain, which is that of `errc` (POSIX).
51 using generic_code = status_code<_generic_code_domain>;
55 template <class StatusCode> class indirecting_domain;
56 template <class T> struct status_code_sizer
61 template <class To, class From> struct type_erasure_is_safe
63 static constexpr bool value = traits::is_move_bitcopying<From>::value //
64 && (sizeof(status_code_sizer<From>) <= sizeof(status_code_sizer<To>));
66 /* We are severely limited by needing to retain C++ 11 compatibility when doing
67 constexpr string parsing. MSVC lets you throw exceptions within a constexpr
68 evaluation context when exceptions are globally disabled, but won't let you
69 divide by zero, even if never evaluated, ever in constexpr. GCC and clang won't
70 let you throw exceptions, ever, if exceptions are globally disabled. So let's
71 use the trick of divide by zero in constexpr on GCC and clang if and only if
72 exceptions are globally disabled.
75 #pragma GCC diagnostic push
76 #pragma GCC diagnostic ignored "-Wdiv-by-zero"
78 #if defined(__cpp_exceptions) || (defined(_MSC_VER) && !defined(__clang__))
79 #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) throw msg
81 #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) ((void) msg, 1 / 0)
83 constexpr inline unsigned long long parse_hex_byte(char c) { return ('0' <= c && c <= '9') ? (c - '0') : ('a' <= c && c <= 'f') ? (10 + c - 'a') : ('A' <= c && c <= 'F') ? (10 + c - 'A') : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("Invalid character in UUID"); }
84 constexpr inline unsigned long long parse_uuid2(const char *s)
86 return ((parse_hex_byte(s[0]) << 0) | (parse_hex_byte(s[1]) << 4) | (parse_hex_byte(s[2]) << 8) | (parse_hex_byte(s[3]) << 12) | (parse_hex_byte(s[4]) << 16) | (parse_hex_byte(s[5]) << 20) | (parse_hex_byte(s[6]) << 24) | (parse_hex_byte(s[7]) << 28) | (parse_hex_byte(s[9]) << 32) | (parse_hex_byte(s[10]) << 36) |
87 (parse_hex_byte(s[11]) << 40) | (parse_hex_byte(s[12]) << 44) | (parse_hex_byte(s[14]) << 48) | (parse_hex_byte(s[15]) << 52) | (parse_hex_byte(s[16]) << 56) | (parse_hex_byte(s[17]) << 60)) //
89 ((parse_hex_byte(s[19]) << 0) | (parse_hex_byte(s[20]) << 4) | (parse_hex_byte(s[21]) << 8) | (parse_hex_byte(s[22]) << 12) | (parse_hex_byte(s[24]) << 16) | (parse_hex_byte(s[25]) << 20) | (parse_hex_byte(s[26]) << 24) | (parse_hex_byte(s[27]) << 28) | (parse_hex_byte(s[28]) << 32) |
90 (parse_hex_byte(s[29]) << 36) | (parse_hex_byte(s[30]) << 40) | (parse_hex_byte(s[31]) << 44) | (parse_hex_byte(s[32]) << 48) | (parse_hex_byte(s[33]) << 52) | (parse_hex_byte(s[34]) << 56) | (parse_hex_byte(s[35]) << 60));
92 template <size_t N> constexpr inline unsigned long long parse_uuid_from_array(const char (&uuid)[N]) { return (N == 37) ? parse_uuid2(uuid) : ((N == 39) ? parse_uuid2(uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length")); }
93 template <size_t N> constexpr inline unsigned long long parse_uuid_from_pointer(const char *uuid) { return (N == 36) ? parse_uuid2(uuid) : ((N == 38) ? parse_uuid2(uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length")); }
95 #pragma GCC diagnostic pop
97 static constexpr unsigned long long test_uuid_parse = parse_uuid_from_array("430f1201-94fc-06c7-430f-120194fc06c7");
98 //static constexpr unsigned long long test_uuid_parse2 = parse_uuid_from_array("x30f1201-94fc-06c7-430f-120194fc06c7");
101 /*! Abstract base class for a coding domain of a status code.
103 class status_code_domain
105 template <class DomainType> friend class status_code;
106 template <class StatusCode> friend class indirecting_domain;
109 //! Type of the unique id for this domain.
110 using unique_id_type = unsigned long long;
111 /*! (Potentially thread safe) Reference to a message string.
113 Be aware that you cannot add payload to implementations of this class.
114 You get exactly the `void *[3]` array to keep state, this is usually
115 sufficient for a `std::shared_ptr<>` or a `std::string`.
117 You can install a handler to be called when this object is copied,
118 moved and destructed. This takes the form of a C function pointer.
124 using value_type = const char;
126 using size_type = size_t;
128 using pointer = const char *;
129 //! The const pointer type
130 using const_pointer = const char *;
131 //! The iterator type
132 using iterator = const char *;
133 //! The const iterator type
134 using const_iterator = const char *;
137 //! The operation occurring
144 //! The prototype of the handler function. Copies can throw, moves and destructs cannot.
145 using _thunk_spec = void (*)(string_ref *dest, const string_ref *src, _thunk_op op);
148 static void _checking_string_thunk(string_ref *dest, const string_ref *src, _thunk_op /*unused*/) noexcept
152 assert(dest->_thunk == _checking_string_thunk); // NOLINT
153 assert(src == nullptr || src->_thunk == _checking_string_thunk); // NOLINT
159 //! Pointers to beginning and end of character range
160 pointer _begin{}, _end{};
161 //! Three `void*` of state
162 void *_state[3]{}; // at least the size of a shared_ptr
163 //! Handler for when operations occur
164 const _thunk_spec _thunk{nullptr};
166 constexpr explicit string_ref(_thunk_spec thunk) noexcept
172 //! Construct from a C string literal
173 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 explicit string_ref(const char *str, size_type len = static_cast<size_type>(-1), void *state0 = nullptr, void *state1 = nullptr, void *state2 = nullptr,
175 _thunk_spec thunk = _checking_string_thunk
177 _thunk_spec thunk = nullptr
181 , _end((len == static_cast<size_type>(-1)) ? (str + detail::cstrlen(str)) : (str + len))
183 _state{state0, state1, state2}
187 //! Copy construct the derived implementation.
188 string_ref(const string_ref &o)
191 , _state{o._state[0], o._state[1], o._state[2]}
194 if(_thunk != nullptr)
196 _thunk(this, &o, _thunk_op::copy);
199 //! Move construct the derived implementation.
200 string_ref(string_ref &&o) noexcept
203 , _state{o._state[0], o._state[1], o._state[2]}
206 if(_thunk != nullptr)
208 _thunk(this, &o, _thunk_op::move);
212 string_ref &operator=(const string_ref &o)
216 #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
217 string_ref temp(static_cast<string_ref &&>(*this));
221 new(this) string_ref(o); // may throw
225 new(this) string_ref(static_cast<string_ref &&>(temp));
230 new(this) string_ref(o);
236 string_ref &operator=(string_ref &&o) noexcept
241 new(this) string_ref(static_cast<string_ref &&>(o));
248 if(_thunk != nullptr)
250 _thunk(this, nullptr, _thunk_op::destruct);
252 _begin = _end = nullptr;
255 //! Returns whether the reference is empty or not
256 BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD bool empty() const noexcept { return _begin == _end; }
257 //! Returns the size of the string
258 size_type size() const noexcept { return _end - _begin; }
259 //! Returns a null terminated C string
260 const_pointer c_str() const noexcept { return _begin; }
261 //! Returns a null terminated C string
262 const_pointer data() const noexcept { return _begin; }
263 //! Returns the beginning of the string
264 iterator begin() noexcept { return _begin; }
265 //! Returns the beginning of the string
266 const_iterator begin() const noexcept { return _begin; }
267 //! Returns the beginning of the string
268 const_iterator cbegin() const noexcept { return _begin; }
269 //! Returns the end of the string
270 iterator end() noexcept { return _end; }
271 //! Returns the end of the string
272 const_iterator end() const noexcept { return _end; }
273 //! Returns the end of the string
274 const_iterator cend() const noexcept { return _end; }
277 /*! A reference counted, threadsafe reference to a message string.
279 class atomic_refcounted_string_ref : public string_ref
281 struct _allocated_msg
283 mutable std::atomic<unsigned> count{1};
285 _allocated_msg *&_msg() noexcept { return reinterpret_cast<_allocated_msg *&>(this->_state[0]); } // NOLINT
286 const _allocated_msg *_msg() const noexcept { return reinterpret_cast<const _allocated_msg *>(this->_state[0]); } // NOLINT
288 static void _refcounted_string_thunk(string_ref *_dest, const string_ref *_src, _thunk_op op) noexcept
290 auto dest = static_cast<atomic_refcounted_string_ref *>(_dest); // NOLINT
291 auto src = static_cast<const atomic_refcounted_string_ref *>(_src); // NOLINT
293 assert(dest->_thunk == _refcounted_string_thunk); // NOLINT
294 assert(src == nullptr || src->_thunk == _refcounted_string_thunk); // NOLINT
297 case _thunk_op::copy:
299 if(dest->_msg() != nullptr)
301 auto count = dest->_msg()->count.fetch_add(1, std::memory_order_relaxed);
303 assert(count != 0); // NOLINT
307 case _thunk_op::move:
309 assert(src); // NOLINT
310 auto msrc = const_cast<atomic_refcounted_string_ref *>(src); // NOLINT
311 msrc->_begin = msrc->_end = nullptr;
312 msrc->_state[0] = msrc->_state[1] = msrc->_state[2] = nullptr;
315 case _thunk_op::destruct:
317 if(dest->_msg() != nullptr)
319 auto count = dest->_msg()->count.fetch_sub(1, std::memory_order_release);
322 std::atomic_thread_fence(std::memory_order_acquire);
323 free((void *) dest->_begin); // NOLINT
324 delete dest->_msg(); // NOLINT
332 //! Construct from a C string literal allocated using `malloc()`.
333 explicit atomic_refcounted_string_ref(const char *str, size_type len = static_cast<size_type>(-1), void *state1 = nullptr, void *state2 = nullptr) noexcept
334 : string_ref(str, len, new(std::nothrow) _allocated_msg, state1, state2, _refcounted_string_thunk)
336 if(_msg() == nullptr)
338 free((void *) this->_begin); // NOLINT
339 _msg() = nullptr; // disabled
340 this->_begin = "failed to get message from system";
341 this->_end = strchr(this->_begin, 0);
351 /*! Use [https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h](https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h) to get a random 64 bit id.
353 Do NOT make up your own value. Do NOT use zero.
355 constexpr explicit status_code_domain(unique_id_type id) noexcept
359 /*! UUID constructor, where input is constexpr parsed into a `unique_id_type`.
362 constexpr explicit status_code_domain(const char (&uuid)[N]) noexcept
363 : _id(detail::parse_uuid_from_array<N>(uuid))
366 template <size_t N> struct _uuid_size
369 //! Alternative UUID constructor
371 constexpr explicit status_code_domain(const char *uuid, _uuid_size<N> /*unused*/) noexcept
372 : _id(detail::parse_uuid_from_pointer<N>(uuid))
375 //! No public copying at type erased level
376 status_code_domain(const status_code_domain &) = default;
377 //! No public moving at type erased level
378 status_code_domain(status_code_domain &&) = default;
379 //! No public assignment at type erased level
380 status_code_domain &operator=(const status_code_domain &) = default;
381 //! No public assignment at type erased level
382 status_code_domain &operator=(status_code_domain &&) = default;
383 //! No public destruction at type erased level
384 ~status_code_domain() = default;
387 //! True if the unique ids match.
388 constexpr bool operator==(const status_code_domain &o) const noexcept { return _id == o._id; }
389 //! True if the unique ids do not match.
390 constexpr bool operator!=(const status_code_domain &o) const noexcept { return _id != o._id; }
391 //! True if this unique is lower than the other's unique id.
392 constexpr bool operator<(const status_code_domain &o) const noexcept { return _id < o._id; }
394 //! Returns the unique id used to identify identical category instances.
395 constexpr unique_id_type id() const noexcept { return _id; }
396 //! Name of this category.
397 virtual string_ref name() const noexcept = 0;
400 //! True if code means failure.
401 virtual bool _do_failure(const status_code<void> &code) const noexcept = 0;
402 //! True if code is (potentially non-transitively) equivalent to another code in another domain.
403 virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept = 0;
404 //! Returns the generic code closest to this code, if any.
405 virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0;
406 //! Return a reference to a string textually representing a code.
407 virtual string_ref _do_message(const status_code<void> &code) const noexcept = 0;
408 #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
409 //! Throw a code as a C++ exception.
410 BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const = 0;
412 // Keep a vtable slot for binary compatibility
413 BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> & /*code*/) const { abort(); }
415 // For a `status_code<erased<T>>` only, copy from `src` to `dst`. Default implementation uses `memcpy()`.
416 virtual void _do_erased_copy(status_code<void> &dst, const status_code<void> &src, size_t bytes) const { memcpy(&dst, &src, bytes); } // NOLINT
417 // For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing.
418 virtual void _do_erased_destroy(status_code<void> &code, size_t bytes) const noexcept // NOLINT
425 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END