123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /* Proposed SG14 status_code
- (C) 2018-2019 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 indirecting_domain;
- template <class T> struct status_code_sizer
- {
- void *a;
- T b;
- };
- template <class To, class From> struct type_erasure_is_safe
- {
- static constexpr bool value = traits::is_move_relocating<From>::value //
- && (sizeof(status_code_sizer<From>) <= sizeof(status_code_sizer<To>));
- };
- } // 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> friend class 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.
- 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.
- 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
- 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
- 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
- ~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 bool empty() const noexcept { return _begin == _end; }
- //! Returns the size of the string
- size_type size() const noexcept { return _end - _begin; }
- //! Returns a null terminated C string
- const_pointer c_str() const noexcept { return _begin; }
- //! Returns a null terminated C string
- const_pointer data() const noexcept { return _begin; }
- //! Returns the beginning of the string
- iterator begin() noexcept { return _begin; }
- //! Returns the beginning of the string
- const_iterator begin() const noexcept { return _begin; }
- //! Returns the beginning of the string
- const_iterator cbegin() const noexcept { return _begin; }
- //! Returns the end of the string
- iterator end() noexcept { return _end; }
- //! Returns the end of the string
- const_iterator end() const noexcept { return _end; }
- //! Returns the end of the string
- 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 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) {}
- //! 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.
- virtual string_ref name() const noexcept = 0;
- protected:
- //! True if code means failure.
- 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.
- 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.
- virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0;
- //! Return a reference to a string textually representing a code.
- 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 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()`.
- virtual void _do_erased_copy(status_code<void> &dst, const status_code<void> &src, size_t bytes) const { memcpy(&dst, &src, bytes); } // NOLINT
- // For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing.
- virtual void _do_erased_destroy(status_code<void> &code, size_t bytes) const noexcept // NOLINT
- {
- (void) code;
- (void) bytes;
- }
- };
- BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
- #endif
|