1/* Proposed SG14 status_code
2(C) 2018-2024 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3File Created: Feb 2018
4
5
6Boost Software License - Version 1.0 - August 17th, 2003
7
8Permission is hereby granted, free of charge, to any person or organization
9obtaining a copy of the software and accompanying documentation covered by
10this license (the "Software") to use, reproduce, display, distribute,
11execute, and transmit the Software, and to prepare derivative works of the
12Software, and to permit third-parties to whom the Software is furnished to
13do so, all subject to the following:
14
15The copyright notices in the Software and this entire statement, including
16the above license grant, this restriction and the following disclaimer,
17must be included in all copies of the Software, in whole or in part, and
18all derivative works of the Software, unless such copies or derivative
19works are solely in the form of machine-executable object code generated by
20a source language processor.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28DEALINGS IN THE SOFTWARE.
29*/
30
31#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_POSIX_CODE_HPP
32#define BOOST_OUTCOME_SYSTEM_ERROR2_POSIX_CODE_HPP
33
34#ifdef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
35#error <posix_code.hpp> is not includable when BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX is defined!
36#endif
37
38#include "quick_status_code_from_enum.hpp"
39
40#include <cstring> // for strchr and strerror_r
41
42BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
43
44// Fix for issue #48 Issue compiling on arm-none-eabi (newlib) with GNU extensions off
45#if !defined(_MSC_VER) && !defined(__APPLE__)
46namespace detail
47{
48 namespace avoid_string_include
49 {
50#if defined(__GLIBC__) && !defined(__UCLIBC__)
51 // This returns int for non-glibc strerror_r, but glibc's is particularly weird so we retain it
52 extern "C" char *strerror_r(int errnum, char *buf, size_t buflen);
53#else
54 extern "C" int strerror_r(int errnum, char *buf, size_t buflen);
55#endif
56 } // namespace avoid_string_include
57} // namespace detail
58#endif
59
60class _posix_code_domain;
61//! A POSIX error code, those returned by `errno`.
62using posix_code = status_code<_posix_code_domain>;
63//! A specialisation of `status_error` for the POSIX error code domain.
64using posix_error = status_error<_posix_code_domain>;
65
66namespace mixins
67{
68 template <class Base> struct mixin<Base, _posix_code_domain> : public Base
69 {
70 using Base::Base;
71
72 //! Returns a `posix_code` for the current value of `errno`.
73 static posix_code current() noexcept;
74 };
75} // namespace mixins
76
77/*! The implementation of the domain for POSIX error codes, those returned by `errno`.
78 */
79class _posix_code_domain : public status_code_domain
80{
81 template <class DomainType> friend class status_code;
82 using _base = status_code_domain;
83
84 static _base::string_ref _make_string_ref(int c) noexcept
85 {
86 char buffer[1024] = "";
87#ifdef _WIN32
88 strerror_s(buffer, sizeof(buffer), c);
89#elif defined(__GLIBC__) && !defined(__UCLIBC__) // handle glibc's weird strerror_r()
90 char *s = detail::avoid_string_include::strerror_r(errnum: c, buf: buffer, buflen: sizeof(buffer)); // NOLINT
91 if(s != nullptr)
92 {
93 strncpy(dest: buffer, src: s, n: sizeof(buffer) - 1); // NOLINT
94 buffer[1023] = 0;
95 }
96#elif !defined(__APPLE__)
97 detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer));
98#else
99 strerror_r(c, buffer, sizeof(buffer));
100#endif
101 size_t length = strlen(s: buffer); // NOLINT
102 auto *p = static_cast<char *>(malloc(size: length + 1)); // NOLINT
103 if(p == nullptr)
104 {
105 return _base::string_ref("failed to get message from system");
106 }
107 memcpy(dest: p, src: buffer, n: length + 1); // NOLINT
108 return _base::atomic_refcounted_string_ref(p, length);
109 }
110
111public:
112 //! The value type of the POSIX code, which is an `int`
113 using value_type = int;
114 using _base::string_ref;
115
116 //! Default constructor
117 constexpr explicit _posix_code_domain(typename _base::unique_id_type id = 0xa59a56fe5f310933) noexcept
118 : _base(id)
119 {
120 }
121 _posix_code_domain(const _posix_code_domain &) = default;
122 _posix_code_domain(_posix_code_domain &&) = default;
123 _posix_code_domain &operator=(const _posix_code_domain &) = default;
124 _posix_code_domain &operator=(_posix_code_domain &&) = default;
125 ~_posix_code_domain() = default;
126
127 //! Constexpr singleton getter. Returns constexpr posix_code_domain variable.
128 static inline constexpr const _posix_code_domain &get();
129
130 virtual string_ref name() const noexcept override { return string_ref("posix domain"); } // NOLINT
131
132 virtual payload_info_t payload_info() const noexcept override
133 {
134 return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
135 (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
136 }
137
138protected:
139 virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
140 {
141 assert(code.domain() == *this); // NOLINT
142 return static_cast<const posix_code &>(code).value() != 0; // NOLINT
143 }
144 virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
145 {
146 assert(code1.domain() == *this); // NOLINT
147 const auto &c1 = static_cast<const posix_code &>(code1); // NOLINT
148 if(code2.domain() == *this)
149 {
150 const auto &c2 = static_cast<const posix_code &>(code2); // NOLINT
151 return c1.value() == c2.value();
152 }
153 if(code2.domain() == generic_code_domain)
154 {
155 const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
156 if(static_cast<int>(c2.value()) == c1.value())
157 {
158 return true;
159 }
160 }
161 return false;
162 }
163 virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
164 {
165 assert(code.domain() == *this); // NOLINT
166 const auto &c = static_cast<const posix_code &>(code); // NOLINT
167 return generic_code(static_cast<errc>(c.value()));
168 }
169 virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
170 {
171 assert(code.domain() == *this); // NOLINT
172 const auto &c = static_cast<const posix_code &>(code); // NOLINT
173 return _make_string_ref(c: c.value());
174 }
175#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
176 BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
177 {
178 assert(code.domain() == *this); // NOLINT
179 const auto &c = static_cast<const posix_code &>(code); // NOLINT
180 throw status_error<_posix_code_domain>(c);
181 }
182#endif
183};
184//! A constexpr source variable for the POSIX code domain, which is that of `errno`. Returned by `_posix_code_domain::get()`.
185constexpr _posix_code_domain posix_code_domain;
186inline constexpr const _posix_code_domain &_posix_code_domain::get()
187{
188 return posix_code_domain;
189}
190
191namespace mixins
192{
193 template <class Base> inline posix_code mixin<Base, _posix_code_domain>::current() noexcept
194 {
195 return posix_code(errno);
196 }
197} // namespace mixins
198
199BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
200
201#endif
202

source code of boost/libs/outcome/include/boost/outcome/experimental/status-code/posix_code.hpp