770 lines
34 KiB
C++
770 lines
34 KiB
C++
|
/* Proposed SG14 status_code
|
||
|
(C) 2018 - 2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
|
||
|
File Created: Feb 2018
|
||
|
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License in the accompanying file
|
||
|
Licence.txt or at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
|
||
|
|
||
|
Distributed under the Boost Software License, Version 1.0.
|
||
|
(See accompanying file Licence.txt or copy at
|
||
|
http://www.boost.org/LICENSE_1_0.txt)
|
||
|
*/
|
||
|
|
||
|
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
|
||
|
#define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_HPP
|
||
|
|
||
|
#include "status_code_domain.hpp"
|
||
|
|
||
|
#if(__cplusplus >= 201700 || _HAS_CXX17) && !defined(BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_STD_IN_PLACE)
|
||
|
// 0.26
|
||
|
#include <utility> // for in_place
|
||
|
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
|
||
|
using in_place_t = std::in_place_t;
|
||
|
using std::in_place;
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
|
||
|
|
||
|
#else
|
||
|
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
|
||
|
//! Aliases `std::in_place_t` if on C++ 17 or later, else defined locally.
|
||
|
struct in_place_t
|
||
|
{
|
||
|
explicit in_place_t() = default;
|
||
|
};
|
||
|
//! Aliases `std::in_place` if on C++ 17 or later, else defined locally.
|
||
|
constexpr in_place_t in_place{};
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
|
||
|
#endif
|
||
|
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
|
||
|
|
||
|
//! Namespace for user injected mixins
|
||
|
namespace mixins
|
||
|
{
|
||
|
template <class Base, class T> struct mixin : public Base
|
||
|
{
|
||
|
using Base::Base;
|
||
|
};
|
||
|
} // namespace mixins
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) //
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(traits::is_move_bitcopying<ErasedType>::value))
|
||
|
struct erased
|
||
|
{
|
||
|
using value_type = ErasedType;
|
||
|
};
|
||
|
} // namespace detail
|
||
|
|
||
|
/*! The tag type used to specialise erased editions of `status_code<D>`.
|
||
|
Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
|
||
|
*/
|
||
|
template <class ErasedType> //
|
||
|
using status_code_erased_tag_type = detail::erased<ErasedType>;
|
||
|
|
||
|
/*! Specialise this template to quickly wrap a third party enumeration into a
|
||
|
custom status code domain.
|
||
|
|
||
|
Use like this:
|
||
|
|
||
|
```c++
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
|
||
|
template <> struct quick_status_code_from_enum<AnotherCode> : quick_status_code_from_enum_defaults<AnotherCode>
|
||
|
{
|
||
|
// Text name of the enum
|
||
|
static constexpr const auto domain_name = "Another Code";
|
||
|
// Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h
|
||
|
static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}";
|
||
|
// Map of each enum value to its text string, and list of semantically equivalent errc's
|
||
|
static const std::initializer_list<mapping> &value_mappings()
|
||
|
{
|
||
|
static const std::initializer_list<mapping<AnotherCode>> v = {
|
||
|
// Format is: { enum value, "string representation", { list of errc mappings ... } }
|
||
|
{AnotherCode::success1, "Success 1", {errc::success}}, //
|
||
|
{AnotherCode::goaway, "Go away", {errc::permission_denied}}, //
|
||
|
{AnotherCode::success2, "Success 2", {errc::success}}, //
|
||
|
{AnotherCode::error2, "Error 2", {}}, //
|
||
|
};
|
||
|
return v;
|
||
|
}
|
||
|
// Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted.
|
||
|
template <class Base> struct mixin : Base
|
||
|
{
|
||
|
using Base::Base;
|
||
|
constexpr int custom_method() const { return 42; }
|
||
|
};
|
||
|
};
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
|
||
|
```
|
||
|
|
||
|
Note that if the `errc` mapping contains `errc::success`, then
|
||
|
the enumeration value is considered to be a successful value.
|
||
|
Otherwise it is considered to be a failure value.
|
||
|
|
||
|
The first value in the `errc` mapping is the one chosen as the
|
||
|
`generic_code` conversion. Other values are used during equivalence
|
||
|
comparisons.
|
||
|
*/
|
||
|
template <class Enum> struct quick_status_code_from_enum;
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
template <class T> struct is_status_code
|
||
|
{
|
||
|
static constexpr bool value = false;
|
||
|
};
|
||
|
template <class T> struct is_status_code<status_code<T>>
|
||
|
{
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
template <class T> struct is_erased_status_code
|
||
|
{
|
||
|
static constexpr bool value = false;
|
||
|
};
|
||
|
template <class T> struct is_erased_status_code<status_code<detail::erased<T>>>
|
||
|
{
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
|
||
|
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 8
|
||
|
// From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf
|
||
|
namespace impl
|
||
|
{
|
||
|
template <typename... Ts> struct make_void
|
||
|
{
|
||
|
using type = void;
|
||
|
};
|
||
|
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
|
||
|
template <class...> struct types
|
||
|
{
|
||
|
using type = types;
|
||
|
};
|
||
|
template <template <class...> class T, class types, class = void> struct test_apply
|
||
|
{
|
||
|
using type = void;
|
||
|
};
|
||
|
template <template <class...> class T, class... Ts> struct test_apply<T, types<Ts...>, void_t<T<Ts...>>>
|
||
|
{
|
||
|
using type = T<Ts...>;
|
||
|
};
|
||
|
} // namespace impl
|
||
|
template <template <class...> class T, class... Ts> using test_apply = impl::test_apply<T, impl::types<Ts...>>;
|
||
|
|
||
|
template <class T, class... Args> using get_make_status_code_result = decltype(make_status_code(std::declval<T>(), std::declval<Args>()...));
|
||
|
template <class... Args> using safe_get_make_status_code_result = test_apply<get_make_status_code_result, Args...>;
|
||
|
|
||
|
#else
|
||
|
|
||
|
// ICE avoidance form for GCCs before 8. Note this form doesn't prevent recursive make_status_code ADL instantation,
|
||
|
// so in certain corner cases this will break. On the other hand, more useful than an ICE.
|
||
|
namespace impl
|
||
|
{
|
||
|
template <typename... Ts> struct make_void
|
||
|
{
|
||
|
using type = void;
|
||
|
};
|
||
|
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
|
||
|
template <class...> struct types
|
||
|
{
|
||
|
using type = types;
|
||
|
};
|
||
|
template <typename Types, typename = void> struct make_status_code_rettype
|
||
|
{
|
||
|
using type = void;
|
||
|
};
|
||
|
template <typename... Args> using get_make_status_code_result = decltype(make_status_code(std::declval<Args>()...));
|
||
|
template <typename... Args> struct make_status_code_rettype<types<Args...>, void_t<get_make_status_code_result<Args...>>>
|
||
|
{
|
||
|
using type = get_make_status_code_result<Args...>;
|
||
|
};
|
||
|
} // namespace impl
|
||
|
template <class... Args> struct safe_get_make_status_code_result
|
||
|
{
|
||
|
using type = typename impl::make_status_code_rettype<impl::types<Args...>>::type;
|
||
|
};
|
||
|
#endif
|
||
|
} // namespace detail
|
||
|
|
||
|
//! Trait returning true if the type is a status code.
|
||
|
template <class T> struct is_status_code
|
||
|
{
|
||
|
static constexpr bool value =
|
||
|
detail::is_status_code<typename std::decay<T>::type>::value || detail::is_erased_status_code<typename std::decay<T>::type>::value;
|
||
|
};
|
||
|
|
||
|
/*! A type erased lightweight status code reflecting empty, success, or failure.
|
||
|
Differs from `status_code<erased<>>` by being always available irrespective of
|
||
|
the domain's value type, but cannot be copied, moved, nor destructed. Thus one
|
||
|
always passes this around by const lvalue reference.
|
||
|
*/
|
||
|
template <> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code<void>
|
||
|
{
|
||
|
template <class T> friend class status_code;
|
||
|
|
||
|
public:
|
||
|
//! The type of the domain.
|
||
|
using domain_type = void;
|
||
|
//! The type of the status code.
|
||
|
using value_type = void;
|
||
|
//! The type of a reference to a message string.
|
||
|
using string_ref = typename status_code_domain::string_ref;
|
||
|
|
||
|
protected:
|
||
|
const status_code_domain *_domain{nullptr};
|
||
|
|
||
|
protected:
|
||
|
//! No default construction at type erased level
|
||
|
status_code() = default;
|
||
|
//! No public copying at type erased level
|
||
|
status_code(const status_code &) = default;
|
||
|
//! No public moving at type erased level
|
||
|
status_code(status_code &&) = default;
|
||
|
//! No public assignment at type erased level
|
||
|
status_code &operator=(const status_code &) = default;
|
||
|
//! No public assignment at type erased level
|
||
|
status_code &operator=(status_code &&) = default;
|
||
|
//! No public destruction at type erased level
|
||
|
~status_code() = default;
|
||
|
|
||
|
//! Used to construct a non-empty type erased status code
|
||
|
constexpr explicit status_code(const status_code_domain *v) noexcept
|
||
|
: _domain(v)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Used to work around triggering a ubsan failure. Do NOT remove!
|
||
|
constexpr const status_code_domain *_domain_ptr() const noexcept { return _domain; }
|
||
|
|
||
|
public:
|
||
|
//! Return the status code domain.
|
||
|
constexpr const status_code_domain &domain() const noexcept { return *_domain; }
|
||
|
//! True if the status code is empty.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD constexpr bool empty() const noexcept { return _domain == nullptr; }
|
||
|
|
||
|
//! Return a reference to a string textually representing a code.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref message() const noexcept
|
||
|
{
|
||
|
// Avoid MSVC's buggy ternary operator for expensive to destruct things
|
||
|
if(_domain != nullptr)
|
||
|
{
|
||
|
return _domain->_do_message(*this);
|
||
|
}
|
||
|
return string_ref("(empty)");
|
||
|
}
|
||
|
//! True if code means success.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 bool success() const noexcept { return (_domain != nullptr) ? !_domain->_do_failure(*this) : false; }
|
||
|
//! True if code means failure.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 bool failure() const noexcept { return (_domain != nullptr) ? _domain->_do_failure(*this) : false; }
|
||
|
/*! True if code is strictly (and potentially non-transitively) semantically equivalent to another code in another domain.
|
||
|
Note that usually non-semantic i.e. pure value comparison is used when the other status code has the same domain.
|
||
|
As `equivalent()` will try mapping to generic code, this usually captures when two codes have the same semantic
|
||
|
meaning in `equivalent()`.
|
||
|
*/
|
||
|
template <class T> BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 bool strictly_equivalent(const status_code<T> &o) const noexcept
|
||
|
{
|
||
|
if(_domain && o._domain)
|
||
|
{
|
||
|
return _domain->_do_equivalent(*this, o);
|
||
|
}
|
||
|
// If we are both empty, we are equivalent
|
||
|
if(!_domain && !o._domain)
|
||
|
{
|
||
|
return true; // NOLINT
|
||
|
}
|
||
|
// Otherwise not equivalent
|
||
|
return false;
|
||
|
}
|
||
|
/*! True if code is equivalent, by any means, to another code in another domain (guaranteed transitive).
|
||
|
Firstly `strictly_equivalent()` is run in both directions. If neither succeeds, each domain is asked
|
||
|
for the equivalent generic code and those are compared.
|
||
|
*/
|
||
|
template <class T> BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool equivalent(const status_code<T> &o) const noexcept;
|
||
|
#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 void throw_exception() const
|
||
|
{
|
||
|
_domain->_do_throw_exception(*this);
|
||
|
abort(); // suppress buggy GCC warning
|
||
|
}
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
template <class DomainType> struct get_domain_value_type
|
||
|
{
|
||
|
using domain_type = DomainType;
|
||
|
using value_type = typename domain_type::value_type;
|
||
|
};
|
||
|
template <class ErasedType> struct get_domain_value_type<erased<ErasedType>>
|
||
|
{
|
||
|
using domain_type = status_code_domain;
|
||
|
using value_type = ErasedType;
|
||
|
};
|
||
|
template <class DomainType> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code_storage : public status_code<void>
|
||
|
{
|
||
|
static_assert(!std::is_void<DomainType>::value, "status_code_storage<void> should never occur!");
|
||
|
using _base = status_code<void>;
|
||
|
|
||
|
public:
|
||
|
//! The type of the domain.
|
||
|
using domain_type = typename get_domain_value_type<DomainType>::domain_type;
|
||
|
//! The type of the status code.
|
||
|
using value_type = typename get_domain_value_type<DomainType>::value_type;
|
||
|
//! The type of a reference to a message string.
|
||
|
using string_ref = typename domain_type::string_ref;
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
static_assert(std::is_move_constructible<value_type>::value || std::is_copy_constructible<value_type>::value,
|
||
|
"DomainType::value_type is neither move nor copy constructible!");
|
||
|
static_assert(!std::is_default_constructible<value_type>::value || std::is_nothrow_default_constructible<value_type>::value,
|
||
|
"DomainType::value_type is not nothrow default constructible!");
|
||
|
static_assert(!std::is_move_constructible<value_type>::value || std::is_nothrow_move_constructible<value_type>::value,
|
||
|
"DomainType::value_type is not nothrow move constructible!");
|
||
|
static_assert(std::is_nothrow_destructible<value_type>::value, "DomainType::value_type is not nothrow destructible!");
|
||
|
#endif
|
||
|
|
||
|
// Replace the type erased implementations with type aware implementations for better codegen
|
||
|
//! Return the status code domain.
|
||
|
constexpr const domain_type &domain() const noexcept { return *static_cast<const domain_type *>(this->_domain); }
|
||
|
|
||
|
//! Reset the code to empty.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept
|
||
|
{
|
||
|
this->_value.~value_type();
|
||
|
this->_domain = nullptr;
|
||
|
new(std::addressof(this->_value)) value_type();
|
||
|
}
|
||
|
|
||
|
#if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
|
||
|
//! Return a reference to the `value_type`.
|
||
|
constexpr value_type &value() & noexcept { return this->_value; }
|
||
|
//! Return a reference to the `value_type`.
|
||
|
constexpr value_type &&value() && noexcept { return static_cast<value_type &&>(this->_value); }
|
||
|
#endif
|
||
|
//! Return a reference to the `value_type`.
|
||
|
constexpr const value_type &value() const & noexcept { return this->_value; }
|
||
|
//! Return a reference to the `value_type`.
|
||
|
constexpr const value_type &&value() const && noexcept { return static_cast<const value_type &&>(this->_value); }
|
||
|
|
||
|
protected:
|
||
|
status_code_storage() = default;
|
||
|
status_code_storage(const status_code_storage &) = default;
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage(status_code_storage &&o) noexcept
|
||
|
: _base(static_cast<status_code_storage &&>(o))
|
||
|
, _value(static_cast<status_code_storage &&>(o)._value)
|
||
|
{
|
||
|
o._domain = nullptr;
|
||
|
}
|
||
|
status_code_storage &operator=(const status_code_storage &) = default;
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code_storage &operator=(status_code_storage &&o) noexcept
|
||
|
{
|
||
|
this->~status_code_storage();
|
||
|
new(this) status_code_storage(static_cast<status_code_storage &&>(o));
|
||
|
return *this;
|
||
|
}
|
||
|
~status_code_storage() = default;
|
||
|
|
||
|
value_type _value{};
|
||
|
struct _value_type_constructor
|
||
|
{
|
||
|
};
|
||
|
template <class... Args>
|
||
|
constexpr status_code_storage(_value_type_constructor /*unused*/, const status_code_domain *v, Args &&...args)
|
||
|
: _base(v)
|
||
|
, _value(static_cast<Args &&>(args)...)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class DomainType> struct has_stateful_mixin
|
||
|
{
|
||
|
static constexpr bool value = (sizeof(status_code_storage<DomainType>) != sizeof(mixins::mixin<status_code_storage<DomainType>, DomainType>));
|
||
|
};
|
||
|
|
||
|
template <class ToDomain, class FromDomain> struct domain_value_type_erasure_is_safe
|
||
|
{
|
||
|
using to_value_type = typename get_domain_value_type<ToDomain>::value_type;
|
||
|
using from_value_type = typename get_domain_value_type<FromDomain>::value_type;
|
||
|
static constexpr bool value = traits::is_move_bitcopying<to_value_type>::value //
|
||
|
&& traits::is_move_bitcopying<from_value_type>::value //
|
||
|
&& sizeof(status_code_storage<FromDomain>) <= sizeof(status_code_storage<ToDomain>) //
|
||
|
&& !has_stateful_mixin<FromDomain>::value;
|
||
|
};
|
||
|
template <class ToDomain> struct domain_value_type_erasure_is_safe<ToDomain, void>
|
||
|
{
|
||
|
static constexpr bool value = false;
|
||
|
};
|
||
|
} // namespace detail
|
||
|
|
||
|
namespace traits
|
||
|
{
|
||
|
//! Determines whether the mixin contained in `StatusCode` contains non-static member variables.
|
||
|
template <class StatusCode> using has_stateful_mixin = detail::has_stateful_mixin<typename detail::remove_cvref<StatusCode>::type::value_type>;
|
||
|
|
||
|
//! Determines whether the status code `From` can be type erased into the status code `To`.
|
||
|
template <class To, class From>
|
||
|
using is_type_erasable_to =
|
||
|
detail::domain_value_type_erasure_is_safe<typename detail::remove_cvref<To>::type::domain_type, typename detail::remove_cvref<From>::type::domain_type>;
|
||
|
} // namespace traits
|
||
|
|
||
|
/*! A lightweight, typed, status code reflecting empty, success, or failure.
|
||
|
This is the main workhorse of the system_error2 library. Its characteristics reflect the value type
|
||
|
set by its domain type, so if that value type is move-only or trivial, so is this.
|
||
|
|
||
|
An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
|
||
|
If it is found, and it generates a status code compatible with this status code, implicit construction
|
||
|
is made available.
|
||
|
|
||
|
You may mix in custom member functions and member function overrides by injecting a specialisation of
|
||
|
`mixins::mixin<Base, YourDomainType>`. Your mixin must inherit from `Base`. Your mixin can carry state,
|
||
|
but if it does, it will no longer be possible to construct erased status codes from such unerased status
|
||
|
codes.
|
||
|
*/
|
||
|
template <class DomainType> class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code : public mixins::mixin<detail::status_code_storage<DomainType>, DomainType>
|
||
|
{
|
||
|
template <class T> friend class status_code;
|
||
|
using _base = mixins::mixin<detail::status_code_storage<DomainType>, DomainType>;
|
||
|
|
||
|
public:
|
||
|
//! The type of the domain.
|
||
|
using domain_type = DomainType;
|
||
|
//! The type of the status code.
|
||
|
using value_type = typename domain_type::value_type;
|
||
|
//! The type of a reference to a message string.
|
||
|
using string_ref = typename domain_type::string_ref;
|
||
|
|
||
|
protected:
|
||
|
using _base::_base;
|
||
|
|
||
|
public:
|
||
|
//! Default construction to empty
|
||
|
status_code() = default;
|
||
|
//! Copy constructor
|
||
|
status_code(const status_code &) = default;
|
||
|
//! Move constructor
|
||
|
status_code(status_code &&) = default; // NOLINT
|
||
|
//! Copy assignment
|
||
|
status_code &operator=(const status_code &) = default;
|
||
|
//! Move assignment
|
||
|
status_code &operator=(status_code &&) = default; // NOLINT
|
||
|
~status_code() = default;
|
||
|
|
||
|
//! Return a copy of the code.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code clone() const { return *this; }
|
||
|
|
||
|
/***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
|
||
|
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(
|
||
|
class T, class... Args, //
|
||
|
class MakeStatusCodeResult =
|
||
|
typename detail::safe_get_make_status_code_result<T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
|
||
|
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
|
||
|
&& !std::is_same<typename std::decay<T>::type, in_place_t>::value // not in_place_t
|
||
|
&& is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
|
||
|
&& std::is_constructible<status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
|
||
|
constexpr status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
|
||
|
: status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
|
||
|
{
|
||
|
}
|
||
|
//! Implicit construction from any `quick_status_code_from_enum<Enum>` enumerated type.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Enum, //
|
||
|
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type) // Enumeration has been activated
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible<status_code, QuickStatusCodeType>::value)) // Its status code is compatible
|
||
|
constexpr status_code(Enum &&v) noexcept(std::is_nothrow_constructible<status_code, QuickStatusCodeType>::value) // NOLINT
|
||
|
: status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
|
||
|
{
|
||
|
}
|
||
|
//! Explicit in-place construction. Disables if `domain_type::get()` is not a valid expression.
|
||
|
template <class... Args>
|
||
|
constexpr explicit status_code(in_place_t /*unused */, Args &&...args) noexcept(std::is_nothrow_constructible<value_type, Args &&...>::value)
|
||
|
: _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<Args &&>(args)...)
|
||
|
{
|
||
|
}
|
||
|
//! Explicit in-place construction from initialiser list. Disables if `domain_type::get()` is not a valid expression.
|
||
|
template <class T, class... Args>
|
||
|
constexpr explicit status_code(in_place_t /*unused */, std::initializer_list<T> il,
|
||
|
Args &&...args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args &&...>::value)
|
||
|
: _base(typename _base::_value_type_constructor{}, &domain_type::get(), il, static_cast<Args &&>(args)...)
|
||
|
{
|
||
|
}
|
||
|
//! Explicit copy construction from a `value_type`. Disables if `domain_type::get()` is not a valid expression.
|
||
|
constexpr explicit status_code(const value_type &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
|
||
|
: _base(typename _base::_value_type_constructor{}, &domain_type::get(), v)
|
||
|
{
|
||
|
}
|
||
|
//! Explicit move construction from a `value_type`. Disables if `domain_type::get()` is not a valid expression.
|
||
|
constexpr explicit status_code(value_type &&v) noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
||
|
: _base(typename _base::_value_type_constructor{}, &domain_type::get(), static_cast<value_type &&>(v))
|
||
|
{
|
||
|
}
|
||
|
/*! Explicit construction from an erased status code. Available only if
|
||
|
`value_type` is trivially copyable or move bitcopying, and `sizeof(status_code) <= sizeof(status_code<erased<>>)`.
|
||
|
Does not check if domains are equal.
|
||
|
*/
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class ErasedType) //
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<domain_type, detail::erased<ErasedType>>::value))
|
||
|
constexpr explicit status_code(const status_code<detail::erased<ErasedType>> &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
|
||
|
: status_code(detail::erasure_cast<value_type>(v.value()))
|
||
|
{
|
||
|
#if __cplusplus >= 201400
|
||
|
assert(v.domain() == this->domain());
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//! Return a reference to a string textually representing a code.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 string_ref message() const noexcept
|
||
|
{
|
||
|
// Avoid MSVC's buggy ternary operator for expensive to destruct things
|
||
|
if(this->_domain != nullptr)
|
||
|
{
|
||
|
return string_ref(this->domain()._do_message(*this));
|
||
|
}
|
||
|
return string_ref("(empty)");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
namespace traits
|
||
|
{
|
||
|
template <class DomainType> struct is_move_bitcopying<status_code<DomainType>>
|
||
|
{
|
||
|
static constexpr bool value = is_move_bitcopying<typename DomainType::value_type>::value;
|
||
|
};
|
||
|
} // namespace traits
|
||
|
|
||
|
|
||
|
/*! Type erased, move-only status_code, unlike `status_code<void>` which cannot be moved nor destroyed. Available
|
||
|
only if `erased<>` is available, which is when the domain's type is trivially
|
||
|
copyable or is move relocatable, and if the size of the domain's typed error code is less than or equal to
|
||
|
this erased error code. Copy construction is disabled, but if you want a copy call `.clone()`.
|
||
|
|
||
|
An ADL discovered helper function `make_status_code(T, Args...)` is looked up by one of the constructors.
|
||
|
If it is found, and it generates a status code compatible with this status code, implicit construction
|
||
|
is made available.
|
||
|
*/
|
||
|
template <class ErasedType>
|
||
|
class BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI status_code<detail::erased<ErasedType>>
|
||
|
: public mixins::mixin<detail::status_code_storage<detail::erased<ErasedType>>, detail::erased<ErasedType>>
|
||
|
{
|
||
|
template <class T> friend class status_code;
|
||
|
using _base = mixins::mixin<detail::status_code_storage<detail::erased<ErasedType>>, detail::erased<ErasedType>>;
|
||
|
|
||
|
public:
|
||
|
//! The type of the domain (void, as it is erased).
|
||
|
using domain_type = void;
|
||
|
//! The type of the erased status code.
|
||
|
using value_type = ErasedType;
|
||
|
//! The type of a reference to a message string.
|
||
|
using string_ref = typename _base::string_ref;
|
||
|
|
||
|
public:
|
||
|
//! Default construction to empty
|
||
|
status_code() = default;
|
||
|
//! Copy constructor
|
||
|
status_code(const status_code &) = delete;
|
||
|
//! Move constructor
|
||
|
status_code(status_code &&) = default; // NOLINT
|
||
|
//! Copy assignment
|
||
|
status_code &operator=(const status_code &) = delete;
|
||
|
//! Move assignment
|
||
|
status_code &operator=(status_code &&) = default; // NOLINT
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 ~status_code()
|
||
|
{
|
||
|
if(nullptr != this->_domain)
|
||
|
{
|
||
|
status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
|
||
|
this->_domain->_do_erased_destroy(*this, info);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Return a copy of the erased code by asking the domain to perform the erased copy.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 status_code clone() const
|
||
|
{
|
||
|
if(nullptr == this->_domain)
|
||
|
{
|
||
|
return {};
|
||
|
}
|
||
|
status_code x;
|
||
|
if(!this->_domain->_do_erased_copy(x, *this, this->_domain->payload_info()))
|
||
|
{
|
||
|
abort(); // should not be possible
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
/***** KEEP THESE IN SYNC WITH ERRORED_STATUS_CODE *****/
|
||
|
//! 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
|
||
|
//! status code.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) //
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<detail::erased<ErasedType>, DomainType>::value),
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code<status_code<typename std::decay<DomainType>::type>>::value))
|
||
|
constexpr status_code(const status_code<DomainType> &v) noexcept // NOLINT
|
||
|
: _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast<value_type>(v.value()))
|
||
|
{
|
||
|
}
|
||
|
//! 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
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) //
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe<detail::erased<ErasedType>, DomainType>::value))
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(status_code<DomainType> &&v) noexcept // NOLINT
|
||
|
: _base(typename _base::_value_type_constructor{}, v._domain_ptr(), detail::erasure_cast<value_type>(v.value()))
|
||
|
{
|
||
|
alignas(alignof(typename DomainType::value_type)) char buffer[sizeof(typename DomainType::value_type)];
|
||
|
new(buffer) typename DomainType::value_type(static_cast<status_code<DomainType> &&>(v).value());
|
||
|
// deliberately do not destruct value moved into buffer
|
||
|
(void) buffer;
|
||
|
v._domain = nullptr;
|
||
|
}
|
||
|
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(
|
||
|
class T, class... Args, //
|
||
|
class MakeStatusCodeResult =
|
||
|
typename detail::safe_get_make_status_code_result<T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
|
||
|
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
|
||
|
&& !std::is_same<typename std::decay<T>::type, value_type>::value // not copy/move of value type
|
||
|
&& is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
|
||
|
&& std::is_constructible<status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
|
||
|
constexpr status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
|
||
|
: status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//! Implicit construction from any `quick_status_code_from_enum<Enum>` enumerated type.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Enum, //
|
||
|
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type) // Enumeration has been activated
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible<status_code, QuickStatusCodeType>::value)) // Its status code is compatible
|
||
|
constexpr status_code(Enum &&v) noexcept(std::is_nothrow_constructible<status_code, QuickStatusCodeType>::value) // NOLINT
|
||
|
: status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
|
||
|
//! 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
|
||
|
//! fit into our storage or the source domain's `_do_erased_copy()` refused the copy.
|
||
|
explicit BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(in_place_t, const status_code<void> &v) // NOLINT
|
||
|
: _base(typename _base::_value_type_constructor{}, v._domain_ptr(), value_type{})
|
||
|
{
|
||
|
status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
|
||
|
if(this->_domain->_do_erased_copy(*this, v, info))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
struct _ final : public std::exception
|
||
|
{
|
||
|
virtual const char *what() const noexcept override { return "status_code: source domain's erased copy function returned failure or refusal"; }
|
||
|
};
|
||
|
throw _{};
|
||
|
}
|
||
|
#endif
|
||
|
//! 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
|
||
|
//! storage or the source domain's `_do_erased_copy()` refused the copy.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 status_code(std::nothrow_t, const status_code<void> &v) noexcept // NOLINT
|
||
|
: _base(typename _base::_value_type_constructor{}, v._domain_ptr(), value_type{})
|
||
|
{
|
||
|
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
|
||
|
try
|
||
|
#endif
|
||
|
{
|
||
|
status_code_domain::payload_info_t info{sizeof(value_type), sizeof(status_code), alignof(status_code)};
|
||
|
if(this->_domain->_do_erased_copy(*this, v, info))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
this->_domain = nullptr;
|
||
|
}
|
||
|
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
|
||
|
catch(...)
|
||
|
{
|
||
|
this->_domain = nullptr;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**** By rights ought to be removed in any formal standard ****/
|
||
|
//! Reset the code to empty.
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 void clear() noexcept { *this = status_code(); }
|
||
|
//! Return the erased `value_type` by value.
|
||
|
constexpr value_type value() const noexcept { return this->_value; }
|
||
|
};
|
||
|
|
||
|
/*! An erased type specialisation of `status_code<D>`.
|
||
|
Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
|
||
|
*/
|
||
|
template <class ErasedType> using erased_status_code = status_code<detail::erased<ErasedType>>;
|
||
|
|
||
|
namespace traits
|
||
|
{
|
||
|
template <class ErasedType> struct is_move_bitcopying<status_code<detail::erased<ErasedType>>>
|
||
|
{
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
} // namespace traits
|
||
|
|
||
|
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
|
||
|
|
||
|
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_INLINE_GDB_PRETTY_PRINTERS
|
||
|
#if defined(__ELF__)
|
||
|
__asm__(
|
||
|
".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
|
||
|
".byte 4 /* Python Text */\n"
|
||
|
".ascii \"gdb.inlined-script\\n\"\n"
|
||
|
".ascii \"import gdb.printing\\n\"\n"
|
||
|
".ascii \"import gdb\\n\"\n"
|
||
|
".ascii \"import os\\n\"\n"
|
||
|
|
||
|
".ascii \"def synthesise_gdb_value_from_string(s):\\n\"\n"
|
||
|
".ascii \" '''For when you want to return a synthetic string from children()'''\\n\"\n"
|
||
|
".ascii \" return gdb.Value(s + '\\0').cast(gdb.lookup_type('char').pointer())\\n\"\n"
|
||
|
|
||
|
".ascii \"class StatusCodePrinter(object):\\n\"\n"
|
||
|
".ascii \" '''Print a system_error2::status_code<T>'''\\n\"\n"
|
||
|
|
||
|
".ascii \" def __init__(self, val):\\n\"\n"
|
||
|
".ascii \" self.val = val\\n\"\n"
|
||
|
|
||
|
".ascii \" def children(self):\\n\"\n"
|
||
|
".ascii \" s = str(self.val['_domain'])\\n\"\n"
|
||
|
".ascii \" if 'posix_code_domain' in s or 'generic_code_domain' in s:\\n\"\n"
|
||
|
".ascii \" yield ('msg', synthesise_gdb_value_from_string(str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')'))\\n\"\n"
|
||
|
".ascii \" yield ('domain', self.val['_domain'])\\n\"\n"
|
||
|
".ascii \" yield ('value', self.val['_value'])\\n\"\n"
|
||
|
|
||
|
".ascii \" def display_hint(self):\\n\"\n"
|
||
|
".ascii \" return None\\n\"\n"
|
||
|
|
||
|
".ascii \" def to_string(self):\\n\"\n"
|
||
|
".ascii \" s = str(self.val['_domain'])\\n\"\n"
|
||
|
".ascii \" if 'posix_code_domain' in s or 'generic_code_domain' in s:\\n\"\n"
|
||
|
".ascii \" return str(self.val['_value']) + ' (' + os.strerror(int(self.val['_value'])) + ')'\\n\"\n"
|
||
|
".ascii \" else:\\n\"\n"
|
||
|
".ascii \" return self.val['_value']\\n\"\n"
|
||
|
|
||
|
".ascii \"def build_pretty_printer():\\n\"\n"
|
||
|
".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter('system_error2')\\n\"\n"
|
||
|
".ascii \" pp.add_printer('system_error2::status_code', '^(boost::)?system_error2::status_code<.*>$', StatusCodePrinter)\\n\"\n"
|
||
|
".ascii \" return pp\\n\"\n"
|
||
|
|
||
|
".ascii \"def register_printers(obj = None):\\n\"\n"
|
||
|
".ascii \" gdb.printing.register_pretty_printer(obj, build_pretty_printer(), replace = True)\\n\"\n"
|
||
|
|
||
|
".ascii \"register_printers(gdb.current_objfile())\\n\"\n"
|
||
|
".byte 0\n"
|
||
|
".popsection\n");
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#endif
|