1/* Proposed SG14 status_code
2(C) 2018 - 2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3File Created: Feb 2018
4
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License in the accompanying file
9Licence.txt or at
10
11http://www.apache.org/licenses/LICENSE-2.0
12
13Unless required by applicable law or agreed to in writing, software
14distributed under the License is distributed on an "AS IS" BASIS,
15WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16See the License for the specific language governing permissions and
17limitations under the License.
18
19
20Distributed under the Boost Software License, Version 1.0.
21(See accompanying file Licence.txt or copy at
22http://www.boost.org/LICENSE_1_0.txt)
23*/
24
25#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
26#define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
27
28#include "status_code_domain.hpp"
29
30#if(__cplusplus >= 201700 || _HAS_CXX17) && !defined(BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_STD_IN_PLACE)
31// 0.26
32#include <utility> // for in_place
33
34BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
35using in_place_t = std::in_place_t;
36using std::in_place;
37BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
38
39#else
40
41BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
42//! Aliases `std::in_place_t` if on C++ 17 or later, else defined locally.
43struct in_place_t
44{
45 explicit in_place_t() = default;
46};
47//! Aliases `std::in_place` if on C++ 17 or later, else defined locally.
48constexpr in_place_t in_place{};
49BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
50#endif
51
52BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
53
54//! Namespace for user injected mixins
55namespace mixins
56{
57 template <class Base, class T> struct mixin : public Base
58 {
59 using Base::Base;
60 };
61} // namespace mixins
62
63namespace detail
64{
65 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) //
66 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(traits::is_move_bitcopying<ErasedType>::value))
67 struct erased
68 {
69 using value_type = ErasedType;
70 };
71} // namespace detail
72
73/*! The tag type used to specialise erased editions of `status_code<D>`.
74Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
75*/
76template <class ErasedType> //
77using status_code_erased_tag_type = detail::erased<ErasedType>;
78
79/*! Specialise this template to quickly wrap a third party enumeration into a
80custom status code domain.
81
82Use like this:
83
84```c++
85BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
86template <> struct quick_status_code_from_enum<AnotherCode> : quick_status_code_from_enum_defaults<AnotherCode>
87{
88 // Text name of the enum
89 static constexpr const auto domain_name = "Another Code";
90 // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h
91 static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}";
92 // Map of each enum value to its text string, and list of semantically equivalent errc's
93 static const std::initializer_list<mapping> &value_mappings()
94 {
95 static const std::initializer_list<mapping<AnotherCode>> v = {
96 // Format is: { enum value, "string representation", { list of errc mappings ... } }
97 {AnotherCode::success1, "Success 1", {errc::success}}, //
98 {AnotherCode::goaway, "Go away", {errc::permission_denied}}, //
99 {AnotherCode::success2, "Success 2", {errc::success}}, //
100 {AnotherCode::error2, "Error 2", {}}, //
101 };
102 return v;
103 }
104 // Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted.
105 template <class Base> struct mixin : Base
106 {
107 using Base::Base;
108 constexpr int custom_method() const { return 42; }
109 };
110};
111BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
112```
113
114Note that if the `errc` mapping contains `errc::success`, then
115the enumeration value is considered to be a successful value.
116Otherwise it is considered to be a failure value.
117
118The first value in the `errc` mapping is the one chosen as the
119`generic_code` conversion. Other values are used during equivalence
120comparisons.
121*/
122template <class Enum> struct quick_status_code_from_enum;
123
124namespace detail
125{
126 template <class T> struct is_status_code
127 {
128 static constexpr bool value = false;
129 };
130 template <class T> struct is_status_code<status_code<T>>
131 {
132 static constexpr bool value = true;
133 };
134 template <class T> struct is_erased_status_code
135 {
136 static constexpr bool value = false;
137 };
138 template <class T> struct is_erased_status_code<status_code<detail::erased<T>>>
139 {
140 static constexpr bool value = true;
141 };
142
143#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 8
144 // From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf
145 namespace impl
146 {
147 template <typename... Ts> struct make_void
148 {
149 using type = void;
150 };
151 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
152 template <class...> struct types
153 {
154 using type = types;
155 };
156 template <template <class...> class T, class types, class = void> struct test_apply
157 {
158 using type = void;
159 };
160 template <template <class...> class T, class... Ts> struct test_apply<T, types<Ts...>, void_t<T<Ts...>>>
161 {
162 using type = T<Ts...>;
163 };
164 } // namespace impl
165 template <template <class...> class T, class... Ts> using test_apply = impl::test_apply<T, impl::types<Ts...>>;
166
167 template <class T, class... Args> using get_make_status_code_result = decltype(make_status_code(std::declval<T>(), std::declval<Args>()...));
168 template <class... Args> using safe_get_make_status_code_result = test_apply<get_make_status_code_result, Args...>;
169
170#else
171
172 // ICE avoidance form for GCCs before 8. Note this form doesn't prevent recursive make_status_code ADL instantation,
173 // so in certain corner cases this will break. On the other hand, more useful than an ICE.
174 namespace impl
175 {
176 template <typename... Ts> struct make_void
177 {
178 using type = void;
179 };
180 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
181 template <class...> struct types
182 {
183 using type = types;
184 };
185 template <typename Types, typename = void> struct make_status_code_rettype
186 {
187 using type = void;
188 };
189 template <typename... Args> using get_make_status_code_result = decltype(make_status_code(std::declval<Args>()...));
190 template <typename... Args> struct make_status_code_rettype<types<Args...>, void_t<get_make_status_code_result<Args...>>>
191 {
192 using type = get_make_status_code_result<Args...>;
193 };
194 } // namespace impl
195 template <class... Args> struct safe_get_make_status_code_result
196 {
197 using type = typename impl::make_status_code_rettype<impl::types<Args...>>::type;
198 };
199#endif
200} // namespace detail
201
202//! Trait returning true if the type is a status code.
203template <class T> struct is_status_code
204{
205 static constexpr bool value =
206 detail::is_status_code<typename std::decay<T>::type>::value || detail::is_erased_status_code<typename std::decay<T>::type>::value;
207};
208
209/*! A type erased lightweight status code reflecting empty, success, or failure.
210Differs from `status_code<erased<>>` by being always available irrespective of
211the domain's value type, but cannot be copied, moved, nor destructed. Thus one
212always passes this around by const lvalue reference.
213*/
214template <> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code<void>
215{
216 template <class T> friend class status_code;
217
218public:
219 //! The type of the domain.
220 using domain_type = void;
221 //! The type of the status code.
222 using value_type = void;
223 //! The type of a reference to a message string.
224 using string_ref = typename status_code_domain::string_ref;
225
226protected:
227 const status_code_domain *_domain{nullptr};
228
229protected:
230 //! No default construction at type erased level
231 status_code() = default;
232 //! No public copying at type erased level
233 status_code(const status_code &) = default;
234 //! No public moving at type erased level
235 status_code(status_code &&) = default;
236 //! No public assignment at type erased level
237 status_code &operator=(const status_code &) = default;
238 //! No public assignment at type erased level
239 status_code &operator=(status_code &&) = default;
240 //! No public destruction at type erased level
241 ~status_code() = default;
242
243 //! Used to construct a non-empty type erased status code
244 constexpr explicit status_code(const status_code_domain *v) noexcept
245 : _domain(v)
246 {
247 }
248
249 // Used to work around triggering a ubsan failure. Do NOT remove!
250 constexpr const status_code_domain *_domain_ptr() const noexcept { return _domain; }
251
252public:
253 //! Return the status code domain.
254 constexpr const status_code_domain &domain() const noexcept { return *_domain; }
255 //! True if the status code is empty.
256 BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _domain == nullptr; }
257
258 //! Return a reference to a string textually representing a code.
259 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref message() const noexcept
260 {
261 // Avoid MSVC's buggy ternary operator for expensive to destruct things
262 if(_domain != nullptr)
263 {
264 return _domain->_do_message(code: *this);
265 }
266 return string_ref("(empty)");
267 }
268 //! True if code means success.
269 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 bool success() const noexcept { return (_domain != nullptr) ? !_domain->_do_failure(code: *this) : false; }
270 //! True if code means failure.
271 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 bool failure() const noexcept { return (_domain != nullptr) ? _domain->_do_failure(code: *this) : false; }
272 /*! True if code is strictly (and potentially non-transitively) semantically equivalent to another code in another domain.
273 Note that usually non-semantic i.e. pure value comparison is used when the other status code has the same domain.
274 As `equivalent()` will try mapping to generic code, this usually captures when two codes have the same semantic
275 meaning in `equivalent()`.
276 */
277 template <class T> BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 bool strictly_equivalent(const status_code<T> &o) const noexcept
278 {
279 if(_domain && o._domain)
280 {
281 return _domain->_do_equivalent(code1: *this, code2: o);
282 }
283 // If we are both empty, we are equivalent
284 if(!_domain && !o._domain)
285 {
286 return true; // NOLINT
287 }
288 // Otherwise not equivalent
289 return false;
290 }
291 /*! True if code is equivalent, by any means, to another code in another domain (guaranteed transitive).
292 Firstly `strictly_equivalent()` is run in both directions. If neither succeeds, each domain is asked
293 for the equivalent generic code and those are compared.
294 */
295 template <class T> BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool equivalent(const status_code<T> &o) const noexcept;
296#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
297 //! Throw a code as a C++ exception.
298 BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN void throw_exception() const
299 {
300 _domain->_do_throw_exception(code: *this);
301 abort(); // suppress buggy GCC warning
302 }
303#endif
304};
305
306namespace detail
307{
308 template <class DomainType> struct get_domain_value_type
309 {
310 using domain_type = DomainType;
311 using value_type = typename domain_type::value_type;
312 };
313 template <class ErasedType> struct get_domain_value_type<erased<ErasedType>>
314 {
315 using domain_type = status_code_domain;
316 using value_type = ErasedType;
317 };
318 template <class DomainType> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code_storage : public status_code<void>
319 {
320 static_assert(!std::is_void<DomainType>::value, "status_code_storage<void> should never occur!");
321 using _base = status_code<void>;
322
323 public:
324 //! The type of the domain.
325 using domain_type = typename get_domain_value_type<DomainType>::domain_type;
326 //! The type of the status code.
327 using value_type = typename get_domain_value_type<DomainType>::value_type;
328 //! The type of a reference to a message string.
329 using string_ref = typename domain_type::string_ref;
330
331#ifndef NDEBUG
332 static_assert(std::is_move_constructible<value_type>::value || std::is_copy_constructible<value_type>::value,
333 "DomainType::value_type is neither move nor copy constructible!");
334 static_assert(!std::is_default_constructible<value_type>::value || std::is_nothrow_default_constructible<value_type>::value,
335 "DomainType::value_type is not nothrow default constructible!");
336 static_assert(!std::is_move_constructible<value_type>::value || std::is_nothrow_move_constructible<value_type>::value,
337 "DomainType::value_type is not nothrow move constructible!");
338 static_assert(std::is_nothrow_destructible<value_type>::value, "DomainType::value_type is not nothrow destructible!");
339#endif
340
341 // Replace the type erased implementations with type aware implementations for better codegen
342 //! Return the status code domain.
343 constexpr const domain_type &domain() const noexcept { return *static_cast<const domain_type *>(this->_domain); }
344
345 //! Reset the code to empty.
346 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept
347 {
348 this->_value.~value_type();
349 this->_domain = nullptr;
350 new(std::addressof(this->_value)) value_type();
351 }
352
353#if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
354 //! Return a reference to the `value_type`.
355 constexpr value_type &value() & noexcept { return this->_value; }
356 //! Return a reference to the `value_type`.
357 constexpr value_type &&value() && noexcept { return static_cast<value_type &&>(this->_value); }
358#endif
359 //! Return a reference to the `value_type`.
360 constexpr const value_type &value() const & noexcept { return this->_value; }
361 //! Return a reference to the `value_type`.
362 constexpr const value_type &&value() const && noexcept { return static_cast<const value_type &&>(this->_value); }
363
364 protected:
365 status_code_storage() = default;
366 status_code_storage(const status_code_storage &) = default;
367 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage(status_code_storage &&o) noexcept
368 : _base(static_cast<status_code_storage &&>(o))
369 , _value(static_cast<status_code_storage &&>(o)._value)
370 {
371 o._domain = nullptr;
372 }
373 status_code_storage &operator=(const status_code_storage &) = default;
374 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage &operator=(status_code_storage &&o) noexcept
375 {
376 this->~status_code_storage();
377 new(this) status_code_storage(static_cast<status_code_storage &&>(o));
378 return *this;
379 }
380 ~status_code_storage() = default;
381
382 value_type _value{};
383 struct _value_type_constructor
384 {
385 };
386 template <class... Args>
387 constexpr status_code_storage(_value_type_constructor /*unused*/, const status_code_domain *v, Args &&...args)
388 : _base(v)
389 , _value(static_cast<Args &&>(args)...)
390 {
391 }
392 };
393
394 template <class DomainType> struct has_stateful_mixin
395 {
396 static constexpr bool value = (sizeof(status_code_storage<DomainType>) != sizeof(mixins::mixin<status_code_storage<DomainType>, DomainType>));
397 };
398
399 template <class ToDomain, class FromDomain> struct domain_value_type_erasure_is_safe
400 {
401 using to_value_type = typename get_domain_value_type<ToDomain>::value_type;
402 using from_value_type = typename get_domain_value_type<FromDomain>::value_type;
403 static constexpr bool value = traits::is_move_bitcopying<to_value_type>::value //
404 && traits::is_move_bitcopying<from_value_type>::value //
405 && sizeof(status_code_storage<FromDomain>) <= sizeof(status_code_storage<ToDomain>) //
406 && !has_stateful_mixin<FromDomain>::value;
407 };
408 template <class ToDomain> struct domain_value_type_erasure_is_safe<ToDomain, void>
409 {
410 static constexpr bool value = false;
411 };
412} // namespace detail
413
414namespace traits
415{
416 //! Determines whether the mixin contained in `StatusCode` contains non-static member variables.
417 template <class StatusCode> using has_stateful_mixin = detail::has_stateful_mixin<typename detail::remove_cvref<StatusCode>::type::value_type>;
418
419 //! Determines whether the status code `From` can be type erased into the status code `To`.
420 template <class To, class From>
421 using is_type_erasable_to =
422 detail::domain_value_type_erasure_is_safe<typename detail::remove_cvref<To>::type::domain_type, typename detail::remove_cvref<From>::type::domain_type>;
423} // namespace traits
424
425/*! A lightweight, typed, status code reflecting empty, success, or failure.
426This is the main workhorse of the system_error2 library. Its characteristics reflect the value type
427set by its domain type, so if that value type is move-only or trivial, so is this.
428
429An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
430If it is found, and it generates a status code compatible with this status code, implicit construction
431is made available.
432
433You may mix in custom member functions and member function overrides by injecting a specialisation of
434`mixins::mixin<Base, YourDomainType>`. Your mixin must inherit from `Base`. Your mixin can carry state,
435but if it does, it will no longer be possible to construct erased status codes from such unerased status
436codes.
437*/
438template <class DomainType> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code : public mixins::mixin<detail::status_code_storage<DomainType>, DomainType>
439{
440 template <class T> friend class status_code;
441 using _base = mixins::mixin<detail::status_code_storage<DomainType>, DomainType>;
442
443public:
444 //! The type of the domain.
445 using domain_type = DomainType;
446 //! The type of the status code.
447 using value_type = typename domain_type::value_type;
448 //! The type of a reference to a message string.
449 using string_ref = typename domain_type::string_ref;
450
451protected:
452 using _base::_base;
453
454public:
455 //! Default construction to empty
456 status_code() = default;
457 //! Copy constructor
458 status_code(const status_code &) = default;
459 //! Move constructor
460 status_code(status_code &&) = default; // NOLINT
461 //! Copy assignment
462 status_code &operator=(const status_code &) = default;
463 //! Move assignment
464 status_code &operator=(status_code &&) = default; // NOLINT
465 ~status_code() = default;
466
467 //! Return a copy of the code.
468 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code clone() const { return *this; }
469
470 /***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
471 //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
472 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(
473 class T, class... Args, //
474 class MakeStatusCodeResult =
475 typename detail::safe_get_make_status_code_result<T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
476 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!std::is_same<typename std::decay<T>::type, status_code>::value // not copy/move of self
477 && !std::is_same<typename std::decay<T>::type, in_place_t>::value // not in_place_t
478 && is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
479 && std::is_constructible<status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
480 constexpr status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
481 : status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
482 {
483 }
484 //! Implicit construction from any `quick_status_code_from_enum<Enum>` enumerated type.
485 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Enum, //
486 class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type) // Enumeration has been activated
487 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible<status_code, QuickStatusCodeType>::value)) // Its status code is compatible
488 constexpr status_code(Enum &&v) noexcept(std::is_nothrow_constructible<status_code, QuickStatusCodeType>::value) // NOLINT
489 : status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
490 {
491 }
492 //! Explicit in-place construction. Disables if `domain_type::get()` is not a valid expression.
493 template <class... Args>
494 constexpr explicit status_code(in_place_t /*unused */, Args &&...args) noexcept(std::is_nothrow_constructible<value_type, Args &&...>::value)
495 : _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<Args &&>(args)...)
496 {
497 }
498 //! Explicit in-place construction from initialiser list. Disables if `domain_type::get()` is not a valid expression.
499 template <class T, class... Args>
500 constexpr explicit status_code(in_place_t /*unused */, std::initializer_list<T> il,
501 Args &&...args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args &&...>::value)
502 : _base(typename _base::_value_type_constructor{}, &domain_type::get(), il, static_cast<Args &&>(args)...)
503 {
504 }
505 //! Explicit copy construction from a `value_type`. Disables if `domain_type::get()` is not a valid expression.
506 constexpr explicit status_code(const value_type &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
507 : _base(typename _base::_value_type_constructor{}, &domain_type::get(), v)
508 {
509 }
510 //! Explicit move construction from a `value_type`. Disables if `domain_type::get()` is not a valid expression.
511 constexpr explicit status_code(value_type &&v) noexcept(std::is_nothrow_move_constructible<value_type>::value)
512 : _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<value_type &&>(v))
513 {
514 }
515 /*! Explicit construction from an erased status code. Available only if
516 `value_type` is trivially copyable or move bitcopying, and `sizeof(status_code) <= sizeof(status_code<erased<>>)`.
517 Does not check if domains are equal.
518 */
519 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) //
520 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<domain_type, detail::erased<ErasedType>>::value))
521 constexpr explicit status_code(const status_code<detail::erased<ErasedType>> &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
522 : status_code(detail::erasure_cast<value_type>(v.value()))
523 {
524#if __cplusplus >= 201400
525 assert(v.domain() == this->domain());
526#endif
527 }
528
529 //! Return a reference to a string textually representing a code.
530 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref message() const noexcept
531 {
532 // Avoid MSVC's buggy ternary operator for expensive to destruct things
533 if(this->_domain != nullptr)
534 {
535 return string_ref(this->domain()._do_message(*this));
536 }
537 return string_ref("(empty)");
538 }
539};
540
541namespace traits
542{
543 template <class DomainType> struct is_move_bitcopying<status_code<DomainType>>
544 {
545 static constexpr bool value = is_move_bitcopying<typename DomainType::value_type>::value;
546 };
547} // namespace traits
548
549
550/*! Type erased, move-only status_code, unlike `status_code<void>` which cannot be moved nor destroyed. Available
551only if `erased<>` is available, which is when the domain's type is trivially
552copyable or is move relocatable, and if the size of the domain's typed error code is less than or equal to
553this erased error code. Copy construction is disabled, but if you want a copy call `.clone()`.
554
555An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
556If it is found, and it generates a status code compatible with this status code, implicit construction
557is made available.
558*/
559template <class ErasedType>
560class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code<detail::erased<ErasedType>>
561 : public mixins::mixin<detail::status_code_storage<detail::erased<ErasedType>>, detail::erased<ErasedType>>
562{
563 template <class T> friend class status_code;
564 using _base = mixins::mixin<detail::status_code_storage<detail::erased<ErasedType>>, detail::erased<ErasedType>>;
565
566public:
567 //! The type of the domain (void, as it is erased).
568 using domain_type = void;
569 //! The type of the erased status code.
570 using value_type = ErasedType;
571 //! The type of a reference to a message string.
572 using string_ref = typename _base::string_ref;
573
574public:
575 //! Default construction to empty
576 status_code() = default;
577 //! Copy constructor
578 status_code(const status_code &) = delete;
579 //! Move constructor
580 status_code(status_code &&) = default; // NOLINT
581 //! Copy assignment
582 status_code &operator=(const status_code &) = delete;
583 //! Move assignment
584 status_code &operator=(status_code &&) = default; // NOLINT
585 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 ~status_code()
586 {
587 if(nullptr != this->_domain)
588 {
589 status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
590 this->_domain->_do_erased_destroy(*this, info);
591 }
592 }
593
594 //! Return a copy of the erased code by asking the domain to perform the erased copy.
595 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 status_code clone() const
596 {
597 if(nullptr == this->_domain)
598 {
599 return {};
600 }
601 status_code x;
602 if(!this->_domain->_do_erased_copy(x, *this, this->_domain->payload_info()))
603 {
604 abort(); // should not be possible
605 }
606 return x;
607 }
608
609 /***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
610 //! Implicit copy construction from any other status code if its value type is trivially copyable, it would fit into our storage, and it is not an erased
611 //! status code.
612 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) //
613 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<detail::erased<ErasedType>, DomainType>::value),
614 BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code<status_code<typename std::decay<DomainType>::type>>::value))
615 constexpr status_code(const status_code<DomainType> &v) noexcept // NOLINT
616 : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast<value_type>(v.value()))
617 {
618 }
619 //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying and it would fit into our storage
620 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) //
621 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<detail::erased<ErasedType>, DomainType>::value))
622 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(status_code<DomainType> &&v) noexcept // NOLINT
623 : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast<value_type>(v.value()))
624 {
625 alignas(alignof(typename DomainType::value_type)) char buffer[sizeof(typename DomainType::value_type)];
626 new(buffer) typename DomainType::value_type(static_cast<status_code<DomainType> &&>(v).value());
627 // deliberately do not destruct value moved into buffer
628 (void) buffer;
629 v._domain = nullptr;
630 }
631 //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
632 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(
633 class T, class... Args, //
634 class MakeStatusCodeResult =
635 typename detail::safe_get_make_status_code_result<T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
636 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!std::is_same<typename std::decay<T>::type, status_code>::value // not copy/move of self
637 && !std::is_same<typename std::decay<T>::type, value_type>::value // not copy/move of value type
638 && is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
639 && std::is_constructible<status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
640 constexpr status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
641 : status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
642 {
643 }
644
645 //! Implicit construction from any `quick_status_code_from_enum<Enum>` enumerated type.
646 BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Enum, //
647 class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type) // Enumeration has been activated
648 BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible<status_code, QuickStatusCodeType>::value)) // Its status code is compatible
649 constexpr status_code(Enum &&v) noexcept(std::is_nothrow_constructible<status_code, QuickStatusCodeType>::value) // NOLINT
650 : status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
651 {
652 }
653
654#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
655 //! Explicit copy construction from an unknown status code. Note that this will throw an exception if its value type is not trivially copyable or would not
656 //! fit into our storage or the source domain's `_do_erased_copy()` refused the copy.
657 explicit BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(in_place_t, const status_code<void> &v) // NOLINT
658 : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), value_type{})
659 {
660 status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
661 if(this->_domain->_do_erased_copy(*this, v, info))
662 {
663 return;
664 }
665 struct _ final : public std::exception
666 {
667 virtual const char *what() const noexcept override { return "status_code: source domain's erased copy function returned failure or refusal"; }
668 };
669 throw _{};
670 }
671#endif
672 //! Tagged copy construction from an unknown status code. Note that this will be empty if its value type is not trivially copyable or would not fit into our
673 //! storage or the source domain's `_do_erased_copy()` refused the copy.
674 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 status_code(std::nothrow_t, const status_code<void> &v) noexcept // NOLINT
675 : _base(typename _base::_value_type_constructor{}, v._domain_ptr(), value_type{})
676 {
677#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
678 try
679#endif
680 {
681 status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
682 if(this->_domain->_do_erased_copy(*this, v, info))
683 {
684 return;
685 }
686 this->_domain = nullptr;
687 }
688#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
689 catch(...)
690 {
691 this->_domain = nullptr;
692 }
693#endif
694 }
695
696 /**** By rights ought to be removed in any formal standard ****/
697 //! Reset the code to empty.
698 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept { *this = status_code(); }
699 //! Return the erased `value_type` by value.
700 constexpr value_type value() const noexcept { return this->_value; }
701};
702
703/*! An erased type specialisation of `status_code<D>`.
704Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
705*/
706template <class ErasedType> using erased_status_code = status_code<detail::erased<ErasedType>>;
707
708namespace traits
709{
710 template <class ErasedType> struct is_move_bitcopying<status_code<detail::erased<ErasedType>>>
711 {
712 static constexpr bool value = true;
713 };
714} // namespace traits
715
716BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
717
718#endif
719

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