]>
Commit | Line | Data |
---|---|---|
92f5a8d4 | 1 | /* Proposed SG14 status_code |
f67539c2 | 2 | (C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits) |
92f5a8d4 TL |
3 | File Created: Aug 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_STD_ERROR_CODE_HPP | |
32 | #define BOOST_OUTCOME_SYSTEM_ERROR2_STD_ERROR_CODE_HPP | |
33 | ||
f67539c2 | 34 | #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX |
92f5a8d4 | 35 | #include "posix_code.hpp" |
f67539c2 | 36 | #endif |
92f5a8d4 TL |
37 | |
38 | #if defined(_WIN32) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) | |
39 | #include "win32_code.hpp" | |
40 | #endif | |
41 | ||
42 | #include <system_error> | |
43 | ||
44 | BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN | |
45 | ||
20effc67 TL |
46 | class _std_error_code_domain; |
47 | //! A `status_code` representing exactly a `std::error_code` | |
48 | using std_error_code = status_code<_std_error_code_domain>; | |
49 | ||
50 | namespace mixins | |
92f5a8d4 | 51 | { |
20effc67 | 52 | template <class Base> struct mixin<Base, _std_error_code_domain> : public Base |
92f5a8d4 | 53 | { |
20effc67 TL |
54 | using Base::Base; |
55 | ||
56 | //! Implicit constructor from a `std::error_code` | |
57 | inline mixin(std::error_code ec); | |
58 | ||
59 | //! Returns the error code category | |
60 | inline const std::error_category &category() const noexcept; | |
92f5a8d4 | 61 | }; |
20effc67 | 62 | } // namespace mixins |
92f5a8d4 | 63 | |
92f5a8d4 TL |
64 | |
65 | /*! The implementation of the domain for `std::error_code` error codes. | |
20effc67 TL |
66 | */ |
67 | class _std_error_code_domain final : public status_code_domain | |
92f5a8d4 TL |
68 | { |
69 | template <class DomainType> friend class status_code; | |
70 | template <class StatusCode> friend class detail::indirecting_domain; | |
71 | using _base = status_code_domain; | |
20effc67 TL |
72 | using _error_code_type = std::error_code; |
73 | using _error_category_type = std::error_category; | |
74 | ||
75 | std::string _name; | |
92f5a8d4 | 76 | |
20effc67 | 77 | static _base::string_ref _make_string_ref(_error_code_type c) noexcept |
92f5a8d4 TL |
78 | { |
79 | try | |
80 | { | |
81 | std::string msg = c.message(); | |
82 | auto *p = static_cast<char *>(malloc(msg.size() + 1)); // NOLINT | |
83 | if(p == nullptr) | |
84 | { | |
85 | return _base::string_ref("failed to allocate message"); | |
86 | } | |
87 | memcpy(p, msg.c_str(), msg.size() + 1); | |
88 | return _base::atomic_refcounted_string_ref(p, msg.size()); | |
89 | } | |
90 | catch(...) | |
91 | { | |
92 | return _base::string_ref("failed to allocate message"); | |
93 | } | |
94 | } | |
95 | ||
96 | public: | |
20effc67 TL |
97 | //! The value type of the `std::error_code` code, which stores the `int` from the `std::error_code` |
98 | using value_type = int; | |
92f5a8d4 TL |
99 | using _base::string_ref; |
100 | ||
20effc67 TL |
101 | //! Returns the error category singleton pointer this status code domain represents |
102 | const _error_category_type &error_category() const noexcept | |
103 | { | |
104 | auto ptr = 0x223a160d20de97b4 ^ this->id(); | |
105 | return *reinterpret_cast<const _error_category_type *>(ptr); | |
106 | } | |
107 | ||
92f5a8d4 | 108 | //! Default constructor |
20effc67 TL |
109 | explicit _std_error_code_domain(const _error_category_type &category) noexcept |
110 | : _base(0x223a160d20de97b4 ^ reinterpret_cast<_base::unique_id_type>(&category)) | |
111 | , _name("std_error_code_domain(") | |
112 | { | |
113 | _name.append(category.name()); | |
114 | _name.push_back(')'); | |
115 | } | |
116 | _std_error_code_domain(const _std_error_code_domain &) = default; | |
117 | _std_error_code_domain(_std_error_code_domain &&) = default; | |
118 | _std_error_code_domain &operator=(const _std_error_code_domain &) = default; | |
119 | _std_error_code_domain &operator=(_std_error_code_domain &&) = default; | |
120 | ~_std_error_code_domain() = default; | |
92f5a8d4 | 121 | |
20effc67 | 122 | static inline const _std_error_code_domain *get(_error_code_type ec); |
92f5a8d4 | 123 | |
20effc67 | 124 | virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT |
92f5a8d4 | 125 | protected: |
20effc67 TL |
126 | virtual bool _do_failure(const status_code<void> &code) const noexcept override; |
127 | virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override; | |
128 | virtual generic_code _generic_code(const status_code<void> &code) const noexcept override; | |
129 | virtual string_ref _do_message(const status_code<void> &code) const noexcept override; | |
130 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) | |
131 | BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override; | |
132 | #endif | |
133 | }; | |
134 | ||
135 | namespace detail | |
136 | { | |
137 | extern inline _std_error_code_domain *std_error_code_domain_from_category(const std::error_category &category) | |
92f5a8d4 | 138 | { |
20effc67 TL |
139 | static constexpr size_t max_items = 64; |
140 | static struct storage_t | |
92f5a8d4 | 141 | { |
20effc67 TL |
142 | std::atomic<unsigned> _lock; |
143 | union item_t { | |
144 | int _init; | |
145 | _std_error_code_domain domain; | |
146 | constexpr item_t() | |
147 | : _init(0) | |
148 | { | |
149 | } | |
150 | ~item_t() {} | |
151 | } items[max_items]; | |
152 | size_t count{0}; | |
153 | ||
154 | void lock() | |
92f5a8d4 | 155 | { |
20effc67 TL |
156 | while(_lock.exchange(1, std::memory_order_acquire) != 0) |
157 | ; | |
92f5a8d4 | 158 | } |
20effc67 TL |
159 | void unlock() { _lock.store(0, std::memory_order_release); } |
160 | ||
161 | storage_t() {} | |
162 | ~storage_t() | |
163 | { | |
164 | lock(); | |
165 | for(size_t n = 0; n < count; n++) | |
166 | { | |
167 | items[n].domain.~_std_error_code_domain(); | |
168 | } | |
169 | unlock(); | |
170 | } | |
171 | _std_error_code_domain *add(const std::error_category &category) | |
172 | { | |
173 | _std_error_code_domain *ret = nullptr; | |
174 | lock(); | |
175 | for(size_t n = 0; n < count; n++) | |
176 | { | |
177 | if(items[n].domain.error_category() == category) | |
178 | { | |
179 | ret = &items[n].domain; | |
180 | break; | |
181 | } | |
182 | } | |
183 | if(ret == nullptr && count < max_items) | |
184 | { | |
185 | ret = new(&items[count++].domain) _std_error_code_domain(category); | |
186 | } | |
187 | unlock(); | |
188 | return ret; | |
189 | } | |
190 | } storage; | |
191 | return storage.add(category); | |
92f5a8d4 | 192 | } |
20effc67 TL |
193 | } // namespace detail |
194 | ||
195 | namespace mixins | |
196 | { | |
197 | template <class Base> | |
198 | inline mixin<Base, _std_error_code_domain>::mixin(std::error_code ec) | |
199 | : Base(typename Base::_value_type_constructor{}, _std_error_code_domain::get(ec), ec.value()) | |
92f5a8d4 | 200 | { |
92f5a8d4 | 201 | } |
20effc67 TL |
202 | |
203 | template <class Base> inline const std::error_category &mixin<Base, _std_error_code_domain>::category() const noexcept | |
204 | { | |
205 | const auto &domain = static_cast<const _std_error_code_domain &>(this->domain()); | |
206 | return domain.error_category(); | |
207 | }; | |
208 | } // namespace mixins | |
209 | ||
210 | inline const _std_error_code_domain *_std_error_code_domain::get(std::error_code ec) | |
211 | { | |
212 | auto *p = detail::std_error_code_domain_from_category(ec.category()); | |
213 | assert(p != nullptr); | |
214 | if(p == nullptr) | |
92f5a8d4 | 215 | { |
20effc67 | 216 | abort(); |
92f5a8d4 | 217 | } |
20effc67 TL |
218 | return p; |
219 | } | |
220 | ||
221 | ||
222 | inline bool _std_error_code_domain::_do_failure(const status_code<void> &code) const noexcept | |
223 | { | |
224 | assert(code.domain() == *this); | |
225 | return static_cast<const std_error_code &>(code).value() != 0; // NOLINT | |
226 | } | |
227 | ||
228 | inline bool _std_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept | |
229 | { | |
230 | assert(code1.domain() == *this); | |
231 | const auto &c1 = static_cast<const std_error_code &>(code1); // NOLINT | |
232 | const auto &cat1 = c1.category(); | |
233 | // Are we comparing to another wrapped error_code? | |
234 | if(code2.domain() == *this) | |
92f5a8d4 | 235 | { |
20effc67 TL |
236 | const auto &c2 = static_cast<const std_error_code &>(code2); // NOLINT |
237 | const auto &cat2 = c2.category(); | |
238 | // If the error code categories are identical, do literal comparison | |
239 | if(cat1 == cat2) | |
240 | { | |
241 | return c1.value() == c2.value(); | |
242 | } | |
243 | // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition() | |
244 | return false; | |
245 | } | |
246 | // Am I an error code with generic category? | |
247 | if(cat1 == std::generic_category()) | |
248 | { | |
249 | // Convert to generic code, and compare that | |
250 | generic_code _c1(static_cast<errc>(c1.value())); | |
251 | return _c1 == code2; | |
92f5a8d4 | 252 | } |
20effc67 TL |
253 | // Am I an error code with system category? |
254 | if(cat1 == std::system_category()) | |
255 | { | |
256 | // Convert to POSIX or Win32 code, and compare that | |
257 | #ifdef _WIN32 | |
258 | win32_code _c1((win32::DWORD) c1.value()); | |
259 | return _c1 == code2; | |
260 | #elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX) | |
261 | posix_code _c1(c1.value()); | |
262 | return _c1 == code2; | |
92f5a8d4 | 263 | #endif |
20effc67 TL |
264 | } |
265 | return false; | |
266 | } | |
267 | ||
268 | inline generic_code _std_error_code_domain::_generic_code(const status_code<void> &code) const noexcept | |
92f5a8d4 | 269 | { |
20effc67 TL |
270 | assert(code.domain() == *this); |
271 | const auto &c = static_cast<const std_error_code &>(code); // NOLINT | |
272 | // Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc. | |
273 | return generic_code(static_cast<errc>(c.category().default_error_condition(c.value()).value())); | |
92f5a8d4 | 274 | } |
20effc67 TL |
275 | |
276 | inline _std_error_code_domain::string_ref _std_error_code_domain::_do_message(const status_code<void> &code) const noexcept | |
92f5a8d4 | 277 | { |
20effc67 TL |
278 | assert(code.domain() == *this); |
279 | const auto &c = static_cast<const std_error_code &>(code); // NOLINT | |
280 | return _make_string_ref(_error_code_type(c.value(), c.category())); | |
92f5a8d4 TL |
281 | } |
282 | ||
20effc67 TL |
283 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) |
284 | BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN inline void _std_error_code_domain::_do_throw_exception(const status_code<void> &code) const | |
285 | { | |
286 | assert(code.domain() == *this); | |
287 | const auto &c = static_cast<const std_error_code &>(code); // NOLINT | |
288 | throw std::system_error(std::error_code(c.value(), c.category())); | |
289 | } | |
290 | #endif | |
291 | ||
292 | static_assert(sizeof(std_error_code) <= sizeof(void *) * 2, "std_error_code does not fit into a system_code!"); | |
293 | ||
92f5a8d4 TL |
294 | BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END |
295 | ||
20effc67 TL |
296 | // Enable implicit construction of `std_error_code` from `std::error_code`. |
297 | namespace std | |
298 | { | |
299 | inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code make_status_code(error_code c) noexcept { return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::std_error_code(c); } | |
300 | } // namespace std | |
301 | ||
92f5a8d4 | 302 | #endif |