]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/outcome/experimental/status-code/status_code_domain.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / outcome / experimental / status-code / status_code_domain.hpp
1 /* Proposed SG14 status_code
2 (C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3 File Created: Feb 2018
4
5
6 Boost Software License - Version 1.0 - August 17th, 2003
7
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:
14
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.
21
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.
29 */
30
31 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
32 #define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
33
34 #include "config.hpp"
35
36 #include <cstring> // for strchr
37
38 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
39
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>>`).
41
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.
47 */
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>;
52
53 namespace detail
54 {
55 template <class StatusCode> class indirecting_domain;
56 template <class T> struct status_code_sizer
57 {
58 void *a;
59 T b;
60 };
61 template <class To, class From> struct type_erasure_is_safe
62 {
63 static constexpr bool value = traits::is_move_bitcopying<From>::value //
64 && (sizeof(status_code_sizer<From>) <= sizeof(status_code_sizer<To>));
65 };
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.
73 */
74 #ifdef __GNUC__
75 #pragma GCC diagnostic push
76 #pragma GCC diagnostic ignored "-Wdiv-by-zero"
77 #endif
78 #if defined(__cpp_exceptions) || (defined(_MSC_VER) && !defined(__clang__))
79 #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) throw msg
80 #else
81 #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) ((void) msg, 1 / 0)
82 #endif
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)
85 {
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)) //
88 ^ //
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));
91 }
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")); }
94 #ifdef __GNUC__
95 #pragma GCC diagnostic pop
96 #endif
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");
99 } // namespace detail
100
101 /*! Abstract base class for a coding domain of a status code.
102 */
103 class status_code_domain
104 {
105 template <class DomainType> friend class status_code;
106 template <class StatusCode> friend class indirecting_domain;
107
108 public:
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.
112
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`.
116
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.
119 */
120 class string_ref
121 {
122 public:
123 //! The value type
124 using value_type = const char;
125 //! The size type
126 using size_type = size_t;
127 //! The pointer type
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 *;
135
136 protected:
137 //! The operation occurring
138 enum class _thunk_op
139 {
140 copy,
141 move,
142 destruct
143 };
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);
146 #ifndef NDEBUG
147 private:
148 static void _checking_string_thunk(string_ref *dest, const string_ref *src, _thunk_op /*unused*/) noexcept
149 {
150 (void) dest;
151 (void) src;
152 assert(dest->_thunk == _checking_string_thunk); // NOLINT
153 assert(src == nullptr || src->_thunk == _checking_string_thunk); // NOLINT
154 // do nothing
155 }
156
157 protected:
158 #endif
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};
165
166 constexpr explicit string_ref(_thunk_spec thunk) noexcept
167 : _thunk(thunk)
168 {
169 }
170
171 public:
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,
174 #ifndef NDEBUG
175 _thunk_spec thunk = _checking_string_thunk
176 #else
177 _thunk_spec thunk = nullptr
178 #endif
179 ) noexcept
180 : _begin(str)
181 , _end((len == static_cast<size_type>(-1)) ? (str + detail::cstrlen(str)) : (str + len))
182 , // NOLINT
183 _state{state0, state1, state2}
184 , _thunk(thunk)
185 {
186 }
187 //! Copy construct the derived implementation.
188 string_ref(const string_ref &o)
189 : _begin(o._begin)
190 , _end(o._end)
191 , _state{o._state[0], o._state[1], o._state[2]}
192 , _thunk(o._thunk)
193 {
194 if(_thunk != nullptr)
195 {
196 _thunk(this, &o, _thunk_op::copy);
197 }
198 }
199 //! Move construct the derived implementation.
200 string_ref(string_ref &&o) noexcept
201 : _begin(o._begin)
202 , _end(o._end)
203 , _state{o._state[0], o._state[1], o._state[2]}
204 , _thunk(o._thunk)
205 {
206 if(_thunk != nullptr)
207 {
208 _thunk(this, &o, _thunk_op::move);
209 }
210 }
211 //! Copy assignment
212 string_ref &operator=(const string_ref &o)
213 {
214 if(this != &o)
215 {
216 #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
217 string_ref temp(static_cast<string_ref &&>(*this));
218 this->~string_ref();
219 try
220 {
221 new(this) string_ref(o); // may throw
222 }
223 catch(...)
224 {
225 new(this) string_ref(static_cast<string_ref &&>(temp));
226 throw;
227 }
228 #else
229 this->~string_ref();
230 new(this) string_ref(o);
231 #endif
232 }
233 return *this;
234 }
235 //! Move assignment
236 string_ref &operator=(string_ref &&o) noexcept
237 {
238 if(this != &o)
239 {
240 this->~string_ref();
241 new(this) string_ref(static_cast<string_ref &&>(o));
242 }
243 return *this;
244 }
245 //! Destruction
246 ~string_ref()
247 {
248 if(_thunk != nullptr)
249 {
250 _thunk(this, nullptr, _thunk_op::destruct);
251 }
252 _begin = _end = nullptr;
253 }
254
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; }
275 };
276
277 /*! A reference counted, threadsafe reference to a message string.
278 */
279 class atomic_refcounted_string_ref : public string_ref
280 {
281 struct _allocated_msg
282 {
283 mutable std::atomic<unsigned> count{1};
284 };
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
287
288 static void _refcounted_string_thunk(string_ref *_dest, const string_ref *_src, _thunk_op op) noexcept
289 {
290 auto dest = static_cast<atomic_refcounted_string_ref *>(_dest); // NOLINT
291 auto src = static_cast<const atomic_refcounted_string_ref *>(_src); // NOLINT
292 (void) src;
293 assert(dest->_thunk == _refcounted_string_thunk); // NOLINT
294 assert(src == nullptr || src->_thunk == _refcounted_string_thunk); // NOLINT
295 switch(op)
296 {
297 case _thunk_op::copy:
298 {
299 if(dest->_msg() != nullptr)
300 {
301 auto count = dest->_msg()->count.fetch_add(1, std::memory_order_relaxed);
302 (void) count;
303 assert(count != 0); // NOLINT
304 }
305 return;
306 }
307 case _thunk_op::move:
308 {
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;
313 return;
314 }
315 case _thunk_op::destruct:
316 {
317 if(dest->_msg() != nullptr)
318 {
319 auto count = dest->_msg()->count.fetch_sub(1, std::memory_order_release);
320 if(count == 1)
321 {
322 std::atomic_thread_fence(std::memory_order_acquire);
323 free((void *) dest->_begin); // NOLINT
324 delete dest->_msg(); // NOLINT
325 }
326 }
327 }
328 }
329 }
330
331 public:
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)
335 {
336 if(_msg() == nullptr)
337 {
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);
342 return;
343 }
344 }
345 };
346
347 private:
348 unique_id_type _id;
349
350 protected:
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.
352
353 Do NOT make up your own value. Do NOT use zero.
354 */
355 constexpr explicit status_code_domain(unique_id_type id) noexcept
356 : _id(id)
357 {
358 }
359 /*! UUID constructor, where input is constexpr parsed into a `unique_id_type`.
360 */
361 template <size_t N>
362 constexpr explicit status_code_domain(const char (&uuid)[N]) noexcept
363 : _id(detail::parse_uuid_from_array<N>(uuid))
364 {
365 }
366 template <size_t N> struct _uuid_size
367 {
368 };
369 //! Alternative UUID constructor
370 template <size_t N>
371 constexpr explicit status_code_domain(const char *uuid, _uuid_size<N> /*unused*/) noexcept
372 : _id(detail::parse_uuid_from_pointer<N>(uuid))
373 {
374 }
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;
385
386 public:
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; }
393
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;
398
399 protected:
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;
411 #else
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(); }
414 #endif
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
419 {
420 (void) code;
421 (void) bytes;
422 }
423 };
424
425 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
426
427 #endif