123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- /* Proposed SG14 status_code
- (C) 2018-2024 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
- File Created: Feb 2018
- Boost Software License - Version 1.0 - August 17th, 2003
- Permission is hereby granted, free of charge, to any person or organization
- obtaining a copy of the software and accompanying documentation covered by
- this license (the "Software") to use, reproduce, display, distribute,
- execute, and transmit the Software, and to prepare derivative works of the
- Software, and to permit third-parties to whom the Software is furnished to
- do so, all subject to the following:
- The copyright notices in the Software and this entire statement, including
- the above license grant, this restriction and the following disclaimer,
- must be included in all copies of the Software, in whole or in part, and
- all derivative works of the Software, unless such copies or derivative
- works are solely in the form of machine-executable object code generated by
- a source language processor.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
- #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
- #define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_DOMAIN_HPP
- #include "config.hpp"
- #include <cstring> // for strchr
- BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
- /*! 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>>`).
- Be careful of placing these into containers! Equality and inequality operators are
- *semantic* not exact. Therefore two distinct items will test true! To help prevent
- surprise on this, `operator<` and `std::hash<>` are NOT implemented in order to
- trap potential incorrectness. Define your own custom comparison functions for your
- container which perform exact comparisons.
- */
- template <class DomainType> class status_code;
- class _generic_code_domain;
- //! The generic code is a status code with the generic code domain, which is that of `errc` (POSIX).
- using generic_code = status_code<_generic_code_domain>;
- namespace detail
- {
- template <class StatusCode, class Allocator> class indirecting_domain;
- /* We are severely limited by needing to retain C++ 11 compatibility when doing
- constexpr string parsing. MSVC lets you throw exceptions within a constexpr
- evaluation context when exceptions are globally disabled, but won't let you
- divide by zero, even if never evaluated, ever in constexpr. GCC and clang won't
- let you throw exceptions, ever, if exceptions are globally disabled. So let's
- use the trick of divide by zero in constexpr on GCC and clang if and only if
- exceptions are globally disabled.
- */
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdiv-by-zero"
- #endif
- #if defined(__cpp_exceptions) || (defined(_MSC_VER) && !defined(__clang__))
- #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) throw msg
- #else
- #define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) ((void) msg, 1 / 0)
- #endif
- 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");
- }
- constexpr inline unsigned long long parse_uuid2(const char *s)
- {
- 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) | (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)) //
- ^ //
- ((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) | (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));
- }
- 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"));
- }
- 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"));
- }
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
- static constexpr unsigned long long test_uuid_parse = parse_uuid_from_array("430f1201-94fc-06c7-430f-120194fc06c7");
- // static constexpr unsigned long long test_uuid_parse2 = parse_uuid_from_array("x30f1201-94fc-06c7-430f-120194fc06c7");
- } // namespace detail
- /*! Abstract base class for a coding domain of a status code.
- */
- class status_code_domain
- {
- template <class DomainType> friend class status_code;
- template <class StatusCode, class Allocator> friend class detail::indirecting_domain;
- public:
- //! Type of the unique id for this domain.
- using unique_id_type = unsigned long long;
- /*! (Potentially thread safe) Reference to a message string.
- Be aware that you cannot add payload to implementations of this class.
- You get exactly the `void *[3]` array to keep state, this is usually
- sufficient for a `std::shared_ptr<>` or a `std::string`.
- You can install a handler to be called when this object is copied,
- moved and destructed. This takes the form of a C function pointer.
- */
- class string_ref
- {
- public:
- //! The value type
- using value_type = const char;
- //! The size type
- using size_type = size_t;
- //! The pointer type
- using pointer = const char *;
- //! The const pointer type
- using const_pointer = const char *;
- //! The iterator type
- using iterator = const char *;
- //! The const iterator type
- using const_iterator = const char *;
- protected:
- //! The operation occurring
- enum class _thunk_op
- {
- copy,
- move,
- destruct
- };
- //! The prototype of the handler function. Copies can throw, moves and destructs cannot.
- using _thunk_spec = void (*)(string_ref *dest, const string_ref *src, _thunk_op op);
- #ifndef NDEBUG
- private:
- static void _checking_string_thunk(string_ref *dest, const string_ref *src, _thunk_op /*unused*/) noexcept
- {
- (void) dest;
- (void) src;
- assert(dest->_thunk == _checking_string_thunk); // NOLINT
- assert(src == nullptr || src->_thunk == _checking_string_thunk); // NOLINT
- // do nothing
- }
- protected:
- #endif
- //! Pointers to beginning and end of character range
- pointer _begin{}, _end{};
- //! Three `void*` of state
- void *_state[3]{}; // at least the size of a shared_ptr
- //! Handler for when operations occur
- const _thunk_spec _thunk{nullptr};
- constexpr explicit string_ref(_thunk_spec thunk) noexcept
- : _thunk(thunk)
- {
- }
- public:
- //! Construct from a C string literal
- 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,
- #ifndef NDEBUG
- _thunk_spec thunk = _checking_string_thunk
- #else
- _thunk_spec thunk = nullptr
- #endif
- ) noexcept
- : _begin(str)
- , _end((len == static_cast<size_type>(-1)) ? (str + detail::cstrlen(str)) : (str + len))
- , // NOLINT
- _state{state0, state1, state2}
- , _thunk(thunk)
- {
- }
- //! Copy construct the derived implementation.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref(const string_ref &o)
- : _begin(o._begin)
- , _end(o._end)
- , _state{o._state[0], o._state[1], o._state[2]}
- , _thunk(o._thunk)
- {
- if(_thunk != nullptr)
- {
- _thunk(this, &o, _thunk_op::copy);
- }
- }
- //! Move construct the derived implementation.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref(string_ref &&o) noexcept
- : _begin(o._begin)
- , _end(o._end)
- , _state{o._state[0], o._state[1], o._state[2]}
- , _thunk(o._thunk)
- {
- if(_thunk != nullptr)
- {
- _thunk(this, &o, _thunk_op::move);
- }
- }
- //! Copy assignment
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref &operator=(const string_ref &o)
- {
- if(this != &o)
- {
- #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
- string_ref temp(static_cast<string_ref &&>(*this));
- this->~string_ref();
- try
- {
- new(this) string_ref(o); // may throw
- }
- catch(...)
- {
- new(this) string_ref(static_cast<string_ref &&>(temp));
- throw;
- }
- #else
- this->~string_ref();
- new(this) string_ref(o);
- #endif
- }
- return *this;
- }
- //! Move assignment
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref &operator=(string_ref &&o) noexcept
- {
- if(this != &o)
- {
- this->~string_ref();
- new(this) string_ref(static_cast<string_ref &&>(o));
- }
- return *this;
- }
- //! Destruction
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 ~string_ref()
- {
- if(_thunk != nullptr)
- {
- _thunk(this, nullptr, _thunk_op::destruct);
- }
- _begin = _end = nullptr;
- }
- //! Returns whether the reference is empty or not
- BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _begin == _end; }
- //! Returns the size of the string
- constexpr size_type size() const noexcept { return _end - _begin; }
- //! Returns a null terminated C string
- constexpr const_pointer c_str() const noexcept { return _begin; }
- //! Returns a null terminated C string
- constexpr const_pointer data() const noexcept { return _begin; }
- //! Returns the beginning of the string
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator begin() noexcept { return _begin; }
- //! Returns the beginning of the string
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator begin() const noexcept { return _begin; }
- //! Returns the beginning of the string
- constexpr const_iterator cbegin() const noexcept { return _begin; }
- //! Returns the end of the string
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator end() noexcept { return _end; }
- //! Returns the end of the string
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator end() const noexcept { return _end; }
- //! Returns the end of the string
- constexpr const_iterator cend() const noexcept { return _end; }
- };
- /*! A reference counted, threadsafe reference to a message string.
- */
- class atomic_refcounted_string_ref : public string_ref
- {
- struct _allocated_msg
- {
- mutable std::atomic<unsigned> count{1};
- };
- _allocated_msg *&_msg() noexcept { return reinterpret_cast<_allocated_msg *&>(this->_state[0]); } // NOLINT
- const _allocated_msg *_msg() const noexcept { return reinterpret_cast<const _allocated_msg *>(this->_state[0]); } // NOLINT
- static BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 void _refcounted_string_thunk(string_ref *_dest, const string_ref *_src, _thunk_op op) noexcept
- {
- auto dest = static_cast<atomic_refcounted_string_ref *>(_dest); // NOLINT
- auto src = static_cast<const atomic_refcounted_string_ref *>(_src); // NOLINT
- (void) src;
- assert(dest->_thunk == _refcounted_string_thunk); // NOLINT
- assert(src == nullptr || src->_thunk == _refcounted_string_thunk); // NOLINT
- switch(op)
- {
- case _thunk_op::copy:
- {
- if(dest->_msg() != nullptr)
- {
- auto count = dest->_msg()->count.fetch_add(1, std::memory_order_relaxed);
- (void) count;
- assert(count != 0); // NOLINT
- }
- return;
- }
- case _thunk_op::move:
- {
- assert(src); // NOLINT
- auto msrc = const_cast<atomic_refcounted_string_ref *>(src); // NOLINT
- msrc->_begin = msrc->_end = nullptr;
- msrc->_state[0] = msrc->_state[1] = msrc->_state[2] = nullptr;
- return;
- }
- case _thunk_op::destruct:
- {
- if(dest->_msg() != nullptr)
- {
- auto count = dest->_msg()->count.fetch_sub(1, std::memory_order_release);
- if(count == 1)
- {
- std::atomic_thread_fence(std::memory_order_acquire);
- free((void *) dest->_begin); // NOLINT
- delete dest->_msg(); // NOLINT
- }
- }
- }
- }
- }
- public:
- //! Construct from a C string literal allocated using `malloc()`.
- explicit atomic_refcounted_string_ref(const char *str, size_type len = static_cast<size_type>(-1), void *state1 = nullptr, void *state2 = nullptr) noexcept
- : string_ref(str, len, new(std::nothrow) _allocated_msg, state1, state2, _refcounted_string_thunk)
- {
- if(_msg() == nullptr)
- {
- free((void *) this->_begin); // NOLINT
- _msg() = nullptr; // disabled
- this->_begin = "failed to get message from system";
- this->_end = strchr(this->_begin, 0);
- return;
- }
- }
- };
- private:
- unique_id_type _id;
- protected:
- /*! 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.
- Do NOT make up your own value. Do NOT use zero.
- */
- constexpr explicit status_code_domain(unique_id_type id) noexcept
- : _id(id)
- {
- }
- /*! UUID constructor, where input is constexpr parsed into a `unique_id_type`.
- */
- template <size_t N>
- constexpr explicit status_code_domain(const char (&uuid)[N]) noexcept
- : _id(detail::parse_uuid_from_array<N>(uuid))
- {
- }
- template <size_t N> struct _uuid_size
- {
- };
- //! Alternative UUID constructor
- template <size_t N>
- constexpr explicit status_code_domain(const char *uuid, _uuid_size<N> /*unused*/) noexcept
- : _id(detail::parse_uuid_from_pointer<N>(uuid))
- {
- }
- //! No public copying at type erased level
- status_code_domain(const status_code_domain &) = default;
- //! No public moving at type erased level
- status_code_domain(status_code_domain &&) = default;
- //! No public assignment at type erased level
- status_code_domain &operator=(const status_code_domain &) = default;
- //! No public assignment at type erased level
- status_code_domain &operator=(status_code_domain &&) = default;
- //! No public destruction at type erased level
- ~status_code_domain() = default;
- public:
- //! True if the unique ids match.
- constexpr bool operator==(const status_code_domain &o) const noexcept { return _id == o._id; }
- //! True if the unique ids do not match.
- constexpr bool operator!=(const status_code_domain &o) const noexcept { return _id != o._id; }
- //! True if this unique is lower than the other's unique id.
- constexpr bool operator<(const status_code_domain &o) const noexcept { return _id < o._id; }
- //! Returns the unique id used to identify identical category instances.
- constexpr unique_id_type id() const noexcept { return _id; }
- //! Name of this category.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref name() const noexcept = 0;
- //! Information about the payload of the code for this domain
- struct payload_info_t
- {
- size_t payload_size{0}; //!< The payload size in bytes
- size_t total_size{0}; //!< The total status code size in bytes (includes domain pointer and mixins state)
- size_t total_alignment{1}; //!< The total status code alignment in bytes
- payload_info_t() = default;
- constexpr payload_info_t(size_t _payload_size, size_t _total_size, size_t _total_alignment)
- : payload_size(_payload_size)
- , total_size(_total_size)
- , total_alignment(_total_alignment)
- {
- }
- };
- //! Information about this domain's payload
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual payload_info_t payload_info() const noexcept = 0;
- protected:
- //! True if code means failure.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code<void> &code) const noexcept = 0;
- //! True if code is (potentially non-transitively) equivalent to another code in another domain.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept = 0;
- //! Returns the generic code closest to this code, if any.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0;
- //! Return a reference to a string textually representing a code.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref _do_message(const status_code<void> &code) const noexcept = 0;
- #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
- //! Throw a code as a C++ exception.
- BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_throw_exception(const status_code<void> &code) const = 0;
- #else
- // Keep a vtable slot for binary compatibility
- BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> & /*code*/) const { abort(); }
- #endif
- // 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
- // trivially copyable or would not fit.
- virtual bool _do_erased_copy(status_code<void> &dst, const status_code<void> &src, payload_info_t dstinfo) const
- {
- // Note that dst may not have its domain set
- const auto srcinfo = payload_info();
- if(dstinfo.total_size < srcinfo.total_size)
- {
- return false;
- }
- const auto tocopy = (dstinfo.total_size > srcinfo.total_size) ? srcinfo.total_size : dstinfo.total_size;
- memcpy(&dst, &src, tocopy);
- return true;
- } // NOLINT
- // For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing.
- BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_erased_destroy(status_code<void> &code, payload_info_t info) const noexcept // NOLINT
- {
- (void) code;
- (void) info;
- }
- };
- BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
- #endif
|