| 1 | /* Proposed SG14 status_code |
| 2 | (C) 2018-2024 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 |
| 41 | (`status_code<erased<T>>`). |
| 42 | |
| 43 | Be careful of placing these into containers! Equality and inequality operators are |
| 44 | *semantic* not exact. Therefore two distinct items will test true! To help prevent |
| 45 | surprise on this, `operator<` and `std::hash<>` are NOT implemented in order to |
| 46 | trap potential incorrectness. Define your own custom comparison functions for your |
| 47 | container which perform exact comparisons. |
| 48 | */ |
| 49 | template <class DomainType> class status_code; |
| 50 | class _generic_code_domain; |
| 51 | //! The generic code is a status code with the generic code domain, which is that of `errc` (POSIX). |
| 52 | using generic_code = status_code<_generic_code_domain>; |
| 53 | |
| 54 | namespace detail |
| 55 | { |
| 56 | template <class StatusCode, class Allocator> class indirecting_domain; |
| 57 | /* We are severely limited by needing to retain C++ 11 compatibility when doing |
| 58 | constexpr string parsing. MSVC lets you throw exceptions within a constexpr |
| 59 | evaluation context when exceptions are globally disabled, but won't let you |
| 60 | divide by zero, even if never evaluated, ever in constexpr. GCC and clang won't |
| 61 | let you throw exceptions, ever, if exceptions are globally disabled. So let's |
| 62 | use the trick of divide by zero in constexpr on GCC and clang if and only if |
| 63 | exceptions are globally disabled. |
| 64 | */ |
| 65 | #ifdef __GNUC__ |
| 66 | #pragma GCC diagnostic push |
| 67 | #pragma GCC diagnostic ignored "-Wdiv-by-zero" |
| 68 | #endif |
| 69 | #if defined(__cpp_exceptions) || (defined(_MSC_VER) && !defined(__clang__)) |
| 70 | #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) throw msg |
| 71 | #else |
| 72 | #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) ((void) msg, 1 / 0) |
| 73 | #endif |
| 74 | constexpr inline unsigned long long parse_hex_byte(char c) |
| 75 | { |
| 76 | return ('0' <= c && c <= '9') ? (c - '0') : |
| 77 | ('a' <= c && c <= 'f') ? (10 + c - 'a') : |
| 78 | ('A' <= c && c <= 'F') ? (10 + c - 'A') : |
| 79 | BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("Invalid character in UUID" ); |
| 80 | } |
| 81 | constexpr inline unsigned long long parse_uuid2(const char *s) |
| 82 | { |
| 83 | return ((parse_hex_byte(c: s[0]) << 0) | (parse_hex_byte(c: s[1]) << 4) | (parse_hex_byte(c: s[2]) << 8) | (parse_hex_byte(c: s[3]) << 12) | |
| 84 | (parse_hex_byte(c: s[4]) << 16) | (parse_hex_byte(c: s[5]) << 20) | (parse_hex_byte(c: s[6]) << 24) | (parse_hex_byte(c: s[7]) << 28) | |
| 85 | (parse_hex_byte(c: s[9]) << 32) | (parse_hex_byte(c: s[10]) << 36) | (parse_hex_byte(c: s[11]) << 40) | (parse_hex_byte(c: s[12]) << 44) | |
| 86 | (parse_hex_byte(c: s[14]) << 48) | (parse_hex_byte(c: s[15]) << 52) | (parse_hex_byte(c: s[16]) << 56) | (parse_hex_byte(c: s[17]) << 60)) // |
| 87 | ^ // |
| 88 | ((parse_hex_byte(c: s[19]) << 0) | (parse_hex_byte(c: s[20]) << 4) | (parse_hex_byte(c: s[21]) << 8) | (parse_hex_byte(c: s[22]) << 12) | |
| 89 | (parse_hex_byte(c: s[24]) << 16) | (parse_hex_byte(c: s[25]) << 20) | (parse_hex_byte(c: s[26]) << 24) | (parse_hex_byte(c: s[27]) << 28) | |
| 90 | (parse_hex_byte(c: s[28]) << 32) | (parse_hex_byte(c: s[29]) << 36) | (parse_hex_byte(c: s[30]) << 40) | (parse_hex_byte(c: s[31]) << 44) | |
| 91 | (parse_hex_byte(c: s[32]) << 48) | (parse_hex_byte(c: s[33]) << 52) | (parse_hex_byte(c: s[34]) << 56) | (parse_hex_byte(c: s[35]) << 60)); |
| 92 | } |
| 93 | template <size_t N> constexpr inline unsigned long long parse_uuid_from_array(const char (&uuid)[N]) |
| 94 | { |
| 95 | return (N == 37) ? parse_uuid2(uuid) : ((N == 39) ? parse_uuid2(uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length" )); |
| 96 | } |
| 97 | template <size_t N> constexpr inline unsigned long long parse_uuid_from_pointer(const char *uuid) |
| 98 | { |
| 99 | return (N == 36) ? parse_uuid2(s: uuid) : ((N == 38) ? parse_uuid2(s: uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length" )); |
| 100 | } |
| 101 | #ifdef __GNUC__ |
| 102 | #pragma GCC diagnostic pop |
| 103 | #endif |
| 104 | static constexpr unsigned long long test_uuid_parse = parse_uuid_from_array(uuid: "430f1201-94fc-06c7-430f-120194fc06c7" ); |
| 105 | // static constexpr unsigned long long test_uuid_parse2 = parse_uuid_from_array("x30f1201-94fc-06c7-430f-120194fc06c7"); |
| 106 | } // namespace detail |
| 107 | |
| 108 | /*! Abstract base class for a coding domain of a status code. |
| 109 | */ |
| 110 | class status_code_domain |
| 111 | { |
| 112 | template <class DomainType> friend class status_code; |
| 113 | template <class StatusCode, class Allocator> friend class detail::indirecting_domain; |
| 114 | |
| 115 | public: |
| 116 | //! Type of the unique id for this domain. |
| 117 | using unique_id_type = unsigned long long; |
| 118 | /*! (Potentially thread safe) Reference to a message string. |
| 119 | |
| 120 | Be aware that you cannot add payload to implementations of this class. |
| 121 | You get exactly the `void *[3]` array to keep state, this is usually |
| 122 | sufficient for a `std::shared_ptr<>` or a `std::string`. |
| 123 | |
| 124 | You can install a handler to be called when this object is copied, |
| 125 | moved and destructed. This takes the form of a C function pointer. |
| 126 | */ |
| 127 | class string_ref |
| 128 | { |
| 129 | public: |
| 130 | //! The value type |
| 131 | using value_type = const char; |
| 132 | //! The size type |
| 133 | using size_type = size_t; |
| 134 | //! The pointer type |
| 135 | using pointer = const char *; |
| 136 | //! The const pointer type |
| 137 | using const_pointer = const char *; |
| 138 | //! The iterator type |
| 139 | using iterator = const char *; |
| 140 | //! The const iterator type |
| 141 | using const_iterator = const char *; |
| 142 | |
| 143 | protected: |
| 144 | //! The operation occurring |
| 145 | enum class _thunk_op |
| 146 | { |
| 147 | copy, |
| 148 | move, |
| 149 | destruct |
| 150 | }; |
| 151 | //! The prototype of the handler function. Copies can throw, moves and destructs cannot. |
| 152 | using _thunk_spec = void (*)(string_ref *dest, const string_ref *src, _thunk_op op); |
| 153 | #ifndef NDEBUG |
| 154 | private: |
| 155 | static void _checking_string_thunk(string_ref *dest, const string_ref *src, _thunk_op /*unused*/) noexcept |
| 156 | { |
| 157 | (void) dest; |
| 158 | (void) src; |
| 159 | assert(dest->_thunk == _checking_string_thunk); // NOLINT |
| 160 | assert(src == nullptr || src->_thunk == _checking_string_thunk); // NOLINT |
| 161 | // do nothing |
| 162 | } |
| 163 | |
| 164 | protected: |
| 165 | #endif |
| 166 | //! Pointers to beginning and end of character range |
| 167 | pointer _begin{}, _end{}; |
| 168 | //! Three `void*` of state |
| 169 | void *_state[3]{}; // at least the size of a shared_ptr |
| 170 | //! Handler for when operations occur |
| 171 | const _thunk_spec _thunk{nullptr}; |
| 172 | |
| 173 | constexpr explicit string_ref(_thunk_spec thunk) noexcept |
| 174 | : _thunk(thunk) |
| 175 | { |
| 176 | } |
| 177 | |
| 178 | public: |
| 179 | //! Construct from a C string literal |
| 180 | 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, |
| 181 | void *state2 = nullptr, |
| 182 | #ifndef NDEBUG |
| 183 | _thunk_spec thunk = _checking_string_thunk |
| 184 | #else |
| 185 | _thunk_spec thunk = nullptr |
| 186 | #endif |
| 187 | ) noexcept |
| 188 | : _begin(str) |
| 189 | , _end((len == static_cast<size_type>(-1)) ? (str + detail::cstrlen(str)) : (str + len)) |
| 190 | , // NOLINT |
| 191 | _state{state0, state1, state2} |
| 192 | , _thunk(thunk) |
| 193 | { |
| 194 | } |
| 195 | //! Copy construct the derived implementation. |
| 196 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref(const string_ref &o) |
| 197 | : _begin(o._begin) |
| 198 | , _end(o._end) |
| 199 | , _state{o._state[0], o._state[1], o._state[2]} |
| 200 | , _thunk(o._thunk) |
| 201 | { |
| 202 | if(_thunk != nullptr) |
| 203 | { |
| 204 | _thunk(this, &o, _thunk_op::copy); |
| 205 | } |
| 206 | } |
| 207 | //! Move construct the derived implementation. |
| 208 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref(string_ref &&o) noexcept |
| 209 | : _begin(o._begin) |
| 210 | , _end(o._end) |
| 211 | , _state{o._state[0], o._state[1], o._state[2]} |
| 212 | , _thunk(o._thunk) |
| 213 | { |
| 214 | if(_thunk != nullptr) |
| 215 | { |
| 216 | _thunk(this, &o, _thunk_op::move); |
| 217 | } |
| 218 | } |
| 219 | //! Copy assignment |
| 220 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref &operator=(const string_ref &o) |
| 221 | { |
| 222 | if(this != &o) |
| 223 | { |
| 224 | #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) |
| 225 | string_ref temp(static_cast<string_ref &&>(*this)); |
| 226 | this->~string_ref(); |
| 227 | try |
| 228 | { |
| 229 | new(this) string_ref(o); // may throw |
| 230 | } |
| 231 | catch(...) |
| 232 | { |
| 233 | new(this) string_ref(static_cast<string_ref &&>(temp)); |
| 234 | throw; |
| 235 | } |
| 236 | #else |
| 237 | this->~string_ref(); |
| 238 | new(this) string_ref(o); |
| 239 | #endif |
| 240 | } |
| 241 | return *this; |
| 242 | } |
| 243 | //! Move assignment |
| 244 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref &operator=(string_ref &&o) noexcept |
| 245 | { |
| 246 | if(this != &o) |
| 247 | { |
| 248 | this->~string_ref(); |
| 249 | new(this) string_ref(static_cast<string_ref &&>(o)); |
| 250 | } |
| 251 | return *this; |
| 252 | } |
| 253 | //! Destruction |
| 254 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 ~string_ref() |
| 255 | { |
| 256 | if(_thunk != nullptr) |
| 257 | { |
| 258 | _thunk(this, nullptr, _thunk_op::destruct); |
| 259 | } |
| 260 | _begin = _end = nullptr; |
| 261 | } |
| 262 | |
| 263 | //! Returns whether the reference is empty or not |
| 264 | BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _begin == _end; } |
| 265 | //! Returns the size of the string |
| 266 | constexpr size_type size() const noexcept { return _end - _begin; } |
| 267 | //! Returns a null terminated C string |
| 268 | constexpr const_pointer c_str() const noexcept { return _begin; } |
| 269 | //! Returns a null terminated C string |
| 270 | constexpr const_pointer data() const noexcept { return _begin; } |
| 271 | //! Returns the beginning of the string |
| 272 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator begin() noexcept { return _begin; } |
| 273 | //! Returns the beginning of the string |
| 274 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator begin() const noexcept { return _begin; } |
| 275 | //! Returns the beginning of the string |
| 276 | constexpr const_iterator cbegin() const noexcept { return _begin; } |
| 277 | //! Returns the end of the string |
| 278 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator end() noexcept { return _end; } |
| 279 | //! Returns the end of the string |
| 280 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator end() const noexcept { return _end; } |
| 281 | //! Returns the end of the string |
| 282 | constexpr const_iterator cend() const noexcept { return _end; } |
| 283 | }; |
| 284 | |
| 285 | /*! A reference counted, threadsafe reference to a message string. |
| 286 | */ |
| 287 | class atomic_refcounted_string_ref : public string_ref |
| 288 | { |
| 289 | struct _allocated_msg |
| 290 | { |
| 291 | mutable std::atomic<unsigned> count{1}; |
| 292 | }; |
| 293 | _allocated_msg *&_msg() noexcept { return reinterpret_cast<_allocated_msg *&>(this->_state[0]); } // NOLINT |
| 294 | const _allocated_msg *_msg() const noexcept { return reinterpret_cast<const _allocated_msg *>(this->_state[0]); } // NOLINT |
| 295 | |
| 296 | static BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 void _refcounted_string_thunk(string_ref *_dest, const string_ref *_src, _thunk_op op) noexcept |
| 297 | { |
| 298 | auto dest = static_cast<atomic_refcounted_string_ref *>(_dest); // NOLINT |
| 299 | auto src = static_cast<const atomic_refcounted_string_ref *>(_src); // NOLINT |
| 300 | (void) src; |
| 301 | assert(dest->_thunk == _refcounted_string_thunk); // NOLINT |
| 302 | assert(src == nullptr || src->_thunk == _refcounted_string_thunk); // NOLINT |
| 303 | switch(op) |
| 304 | { |
| 305 | case _thunk_op::copy: |
| 306 | { |
| 307 | if(dest->_msg() != nullptr) |
| 308 | { |
| 309 | auto count = dest->_msg()->count.fetch_add(i: 1, m: std::memory_order_relaxed); |
| 310 | (void) count; |
| 311 | assert(count != 0); // NOLINT |
| 312 | } |
| 313 | return; |
| 314 | } |
| 315 | case _thunk_op::move: |
| 316 | { |
| 317 | assert(src); // NOLINT |
| 318 | auto msrc = const_cast<atomic_refcounted_string_ref *>(src); // NOLINT |
| 319 | msrc->_begin = msrc->_end = nullptr; |
| 320 | msrc->_state[0] = msrc->_state[1] = msrc->_state[2] = nullptr; |
| 321 | return; |
| 322 | } |
| 323 | case _thunk_op::destruct: |
| 324 | { |
| 325 | if(dest->_msg() != nullptr) |
| 326 | { |
| 327 | auto count = dest->_msg()->count.fetch_sub(i: 1, m: std::memory_order_release); |
| 328 | if(count == 1) |
| 329 | { |
| 330 | std::atomic_thread_fence(m: std::memory_order_acquire); |
| 331 | free(ptr: (void *) dest->_begin); // NOLINT |
| 332 | delete dest->_msg(); // NOLINT |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | public: |
| 340 | //! Construct from a C string literal allocated using `malloc()`. |
| 341 | explicit atomic_refcounted_string_ref(const char *str, size_type len = static_cast<size_type>(-1), void *state1 = nullptr, void *state2 = nullptr) noexcept |
| 342 | : string_ref(str, len, new(std::nothrow) _allocated_msg, state1, state2, _refcounted_string_thunk) |
| 343 | { |
| 344 | if(_msg() == nullptr) |
| 345 | { |
| 346 | free(ptr: (void *) this->_begin); // NOLINT |
| 347 | _msg() = nullptr; // disabled |
| 348 | this->_begin = "failed to get message from system" ; |
| 349 | this->_end = strchr(s: this->_begin, c: 0); |
| 350 | return; |
| 351 | } |
| 352 | } |
| 353 | }; |
| 354 | |
| 355 | private: |
| 356 | unique_id_type _id; |
| 357 | |
| 358 | protected: |
| 359 | /*! 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. |
| 360 | |
| 361 | Do NOT make up your own value. Do NOT use zero. |
| 362 | */ |
| 363 | constexpr explicit status_code_domain(unique_id_type id) noexcept |
| 364 | : _id(id) |
| 365 | { |
| 366 | } |
| 367 | /*! UUID constructor, where input is constexpr parsed into a `unique_id_type`. |
| 368 | */ |
| 369 | template <size_t N> |
| 370 | constexpr explicit status_code_domain(const char (&uuid)[N]) noexcept |
| 371 | : _id(detail::parse_uuid_from_array<N>(uuid)) |
| 372 | { |
| 373 | } |
| 374 | template <size_t N> struct _uuid_size |
| 375 | { |
| 376 | }; |
| 377 | //! Alternative UUID constructor |
| 378 | template <size_t N> |
| 379 | constexpr explicit status_code_domain(const char *uuid, _uuid_size<N> /*unused*/) noexcept |
| 380 | : _id(detail::parse_uuid_from_pointer<N>(uuid)) |
| 381 | { |
| 382 | } |
| 383 | //! No public copying at type erased level |
| 384 | status_code_domain(const status_code_domain &) = default; |
| 385 | //! No public moving at type erased level |
| 386 | status_code_domain(status_code_domain &&) = default; |
| 387 | //! No public assignment at type erased level |
| 388 | status_code_domain &operator=(const status_code_domain &) = default; |
| 389 | //! No public assignment at type erased level |
| 390 | status_code_domain &operator=(status_code_domain &&) = default; |
| 391 | //! No public destruction at type erased level |
| 392 | ~status_code_domain() = default; |
| 393 | |
| 394 | public: |
| 395 | //! True if the unique ids match. |
| 396 | constexpr bool operator==(const status_code_domain &o) const noexcept { return _id == o._id; } |
| 397 | //! True if the unique ids do not match. |
| 398 | constexpr bool operator!=(const status_code_domain &o) const noexcept { return _id != o._id; } |
| 399 | //! True if this unique is lower than the other's unique id. |
| 400 | constexpr bool operator<(const status_code_domain &o) const noexcept { return _id < o._id; } |
| 401 | |
| 402 | //! Returns the unique id used to identify identical category instances. |
| 403 | constexpr unique_id_type id() const noexcept { return _id; } |
| 404 | //! Name of this category. |
| 405 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref name() const noexcept = 0; |
| 406 | //! Information about the payload of the code for this domain |
| 407 | struct payload_info_t |
| 408 | { |
| 409 | size_t payload_size{0}; //!< The payload size in bytes |
| 410 | size_t total_size{0}; //!< The total status code size in bytes (includes domain pointer and mixins state) |
| 411 | size_t total_alignment{1}; //!< The total status code alignment in bytes |
| 412 | |
| 413 | payload_info_t() = default; |
| 414 | constexpr payload_info_t(size_t _payload_size, size_t _total_size, size_t _total_alignment) |
| 415 | : payload_size(_payload_size) |
| 416 | , total_size(_total_size) |
| 417 | , total_alignment(_total_alignment) |
| 418 | { |
| 419 | } |
| 420 | }; |
| 421 | //! Information about this domain's payload |
| 422 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual payload_info_t payload_info() const noexcept = 0; |
| 423 | |
| 424 | protected: |
| 425 | //! True if code means failure. |
| 426 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code<void> &code) const noexcept = 0; |
| 427 | //! True if code is (potentially non-transitively) equivalent to another code in another domain. |
| 428 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept = 0; |
| 429 | //! Returns the generic code closest to this code, if any. |
| 430 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0; |
| 431 | //! Return a reference to a string textually representing a code. |
| 432 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref _do_message(const status_code<void> &code) const noexcept = 0; |
| 433 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) |
| 434 | //! Throw a code as a C++ exception. |
| 435 | BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_throw_exception(const status_code<void> &code) const = 0; |
| 436 | #else |
| 437 | // Keep a vtable slot for binary compatibility |
| 438 | BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> & /*code*/) const { abort(); } |
| 439 | #endif |
| 440 | // For a `status_code<erased<T>>` only, copy from `src` to `dst`. Default implementation uses `memcpy()`. You should return false here if your payload is not |
| 441 | // trivially copyable or would not fit. |
| 442 | virtual bool _do_erased_copy(status_code<void> &dst, const status_code<void> &src, payload_info_t dstinfo) const |
| 443 | { |
| 444 | // Note that dst may not have its domain set |
| 445 | const auto srcinfo = payload_info(); |
| 446 | if(dstinfo.total_size < srcinfo.total_size) |
| 447 | { |
| 448 | return false; |
| 449 | } |
| 450 | const auto tocopy = (dstinfo.total_size > srcinfo.total_size) ? srcinfo.total_size : dstinfo.total_size; |
| 451 | memcpy(dest: &dst, src: &src, n: tocopy); |
| 452 | return true; |
| 453 | } // NOLINT |
| 454 | // For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing. |
| 455 | BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_erased_destroy(status_code<void> &code, payload_info_t info) const noexcept // NOLINT |
| 456 | { |
| 457 | (void) code; |
| 458 | (void) info; |
| 459 | } |
| 460 | }; |
| 461 | |
| 462 | BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END |
| 463 | |
| 464 | #endif |
| 465 | |