Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
@@ -0,0 +1,334 @@
/* Proposed SG14 status_code
(C) 2018 - 2022 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Mar 2022
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_BOOST_ERROR_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_BOOST_ERROR_CODE_HPP
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
#include "posix_code.hpp"
#endif
#if defined(_WIN32) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#include "win32_code.hpp"
#endif
#include "std_error_code.hpp"
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <system_error>
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
class _boost_error_code_domain;
//! A `status_code` representing exactly a `boost::system::error_code`
using boost_error_code = status_code<_boost_error_code_domain>;
namespace mixins
{
template <class Base> struct mixin<Base, _boost_error_code_domain> : public Base
{
using Base::Base;
//! Implicit constructor from a `boost::system::error_code`
inline mixin(boost::system::error_code ec);
//! Returns the error code category
inline const boost::system::error_category &category() const noexcept;
};
} // namespace mixins
/*! The implementation of the domain for `boost::system::error_code` error codes.
*/
class _boost_error_code_domain final : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
using _error_code_type = boost::system::error_code;
using _error_category_type = boost::system::error_category;
std::string _name;
static _base::string_ref _make_string_ref(_error_code_type c) noexcept
{
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
try
#endif
{
std::string msg = c.message();
auto *p = static_cast<char *>(malloc(msg.size() + 1)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to allocate message");
}
memcpy(p, msg.c_str(), msg.size() + 1);
return _base::atomic_refcounted_string_ref(p, msg.size());
}
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
catch(...)
{
return _base::string_ref("failed to allocate message");
}
#endif
}
public:
//! The value type of the `boost::system::error_code` code, which stores the `int` from the `boost::system::error_code`
using value_type = int;
using _base::string_ref;
//! Returns the error category singleton pointer this status code domain represents
const _error_category_type &error_category() const noexcept
{
auto ptr = 0x0ea88ff382d94915 ^ this->id();
return *reinterpret_cast<const _error_category_type *>(ptr);
}
//! Default constructor
explicit _boost_error_code_domain(const _error_category_type &category) noexcept
: _base(0x0ea88ff382d94915 ^ reinterpret_cast<_base::unique_id_type>(&category))
, _name("boost_error_code_domain(")
{
_name.append(category.name());
_name.push_back(')');
}
_boost_error_code_domain(const _boost_error_code_domain &) = default;
_boost_error_code_domain(_boost_error_code_domain &&) = default;
_boost_error_code_domain &operator=(const _boost_error_code_domain &) = default;
_boost_error_code_domain &operator=(_boost_error_code_domain &&) = default;
~_boost_error_code_domain() = default;
static inline const _boost_error_code_domain *get(_error_code_type ec);
virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override;
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override;
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override;
virtual string_ref _do_message(const status_code<void> &code) const noexcept override;
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override;
#endif
};
namespace detail
{
extern inline _boost_error_code_domain *boost_error_code_domain_from_category(const boost::system::error_category &category)
{
static constexpr size_t max_items = 64;
static struct storage_t
{
std::atomic<unsigned> _lock;
union item_t
{
int _init;
_boost_error_code_domain domain;
constexpr item_t()
: _init(0)
{
}
~item_t() {}
} items[max_items];
size_t count{0};
void lock()
{
while(_lock.exchange(1, std::memory_order_acquire) != 0)
;
}
void unlock() { _lock.store(0, std::memory_order_release); }
storage_t() {}
~storage_t()
{
lock();
for(size_t n = 0; n < count; n++)
{
items[n].domain.~_boost_error_code_domain();
}
unlock();
}
_boost_error_code_domain *add(const boost::system::error_category &category)
{
_boost_error_code_domain *ret = nullptr;
lock();
for(size_t n = 0; n < count; n++)
{
if(items[n].domain.error_category() == category)
{
ret = &items[n].domain;
break;
}
}
if(ret == nullptr && count < max_items)
{
ret = new(BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(items[count++].domain)) _boost_error_code_domain(category);
}
unlock();
return ret;
}
} storage;
return storage.add(category);
}
} // namespace detail
namespace mixins
{
template <class Base>
inline mixin<Base, _boost_error_code_domain>::mixin(boost::system::error_code ec)
: Base(typename Base::_value_type_constructor{}, _boost_error_code_domain::get(ec), ec.value())
{
}
template <class Base> inline const boost::system::error_category &mixin<Base, _boost_error_code_domain>::category() const noexcept
{
const auto &domain = static_cast<const _boost_error_code_domain &>(this->domain());
return domain.error_category();
};
} // namespace mixins
inline const _boost_error_code_domain *_boost_error_code_domain::get(boost::system::error_code ec)
{
auto *p = detail::boost_error_code_domain_from_category(ec.category());
assert(p != nullptr);
if(p == nullptr)
{
abort();
}
return p;
}
inline bool _boost_error_code_domain::_do_failure(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
return static_cast<const boost_error_code &>(code).value() != 0; // NOLINT
}
inline bool _boost_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const boost_error_code &>(code1); // NOLINT
const auto &cat1 = c1.category();
// Are we comparing to another wrapped error_code?
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const boost_error_code &>(code2); // NOLINT
const auto &cat2 = c2.category();
// If the error code categories are identical, do literal comparison
if(cat1 == cat2)
{
return c1.value() == c2.value();
}
// Otherwise fall back onto the _generic_code comparison, which uses default_error_condition()
return false;
}
// Am I an error code with generic category?
if(cat1 == boost::system::generic_category())
{
// Convert to generic code, and compare that
generic_code _c1(static_cast<errc>(c1.value()));
return _c1 == code2;
}
// Am I an error code with system category?
if(cat1 == boost::system::system_category())
{
// Convert to POSIX or Win32 code, and compare that
#ifdef _WIN32
win32_code _c1((win32::DWORD) c1.value());
return _c1 == code2;
#elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX)
posix_code _c1(c1.value());
return _c1 == code2;
#endif
}
return false;
}
inline generic_code _boost_error_code_domain::_generic_code(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
const auto &c = static_cast<const boost_error_code &>(code); // NOLINT
// Ask my embedded error code for its mapping to boost::system::errc, which is a subset of our generic_code errc.
boost::system::error_condition cond(c.category().default_error_condition(c.value()));
if(cond.category() == boost::system::generic_category())
{
return generic_code(static_cast<errc>(cond.value()));
}
#if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32)
if(cond.category() == boost::system::system_category())
{
return generic_code(static_cast<errc>(cond.value()));
}
#endif
return errc::unknown;
}
inline _boost_error_code_domain::string_ref _boost_error_code_domain::_do_message(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
const auto &c = static_cast<const boost_error_code &>(code); // NOLINT
return _make_string_ref(_error_code_type(c.value(), c.category()));
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN inline void _boost_error_code_domain::_do_throw_exception(const status_code<void> &code) const
{
assert(code.domain() == *this);
const auto &c = static_cast<const boost_error_code &>(code); // NOLINT
throw boost::system::system_error(boost::system::error_code(c.value(), c.category()));
}
#endif
static_assert(sizeof(boost_error_code) <= sizeof(void *) * 2, "boost_error_code does not fit into a system_code!");
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
// Enable implicit construction of `boost_error_code` from `boost::system::error_code`.
namespace boost
{
namespace system
{
inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::erased_status_code<int> make_status_code(error_code c) noexcept
{
if(c.category() == detail::interop_category())
{
// This is actually a wrap of std::error_code. If this fails to compile, your Boost is too old.
return std::make_status_code(std::error_code(c));
}
return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::boost_error_code(c);
}
} // namespace system
} // namespace boost
#endif
@@ -0,0 +1,253 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_COM_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_COM_CODE_HPP
#if !defined(_WIN32) && !defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#error This file should only be included on Windows
#endif
#include "nt_code.hpp"
#include "win32_code.hpp"
#ifndef BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE
#include <comdef.h>
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
class _com_code_domain;
/*! (Windows only) A COM error code. Note semantic equivalence testing is only implemented for `FACILITY_WIN32`
and `FACILITY_NT_BIT`. As you can see at
[https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/](https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/), there
are an awful lot of COM error codes, and keeping mapping tables for all of them would be impractical (for the Win32 and NT facilities, we actually reuse the
mapping tables in `win32_code` and `nt_code`). You can, of course, inherit your own COM code domain from this one and override the `_do_equivalent()` function
to add semantic equivalence testing for whichever extra COM codes that your application specifically needs.
*/
using com_code = status_code<_com_code_domain>;
//! (Windows only) A specialisation of `status_error` for the COM error code domain.
using com_error = status_error<_com_code_domain>;
/*! (Windows only) The implementation of the domain for COM error codes and/or `IErrorInfo`.
*/
class _com_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
//! Construct from a `HRESULT` error code
#ifdef _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP
static _base::string_ref _make_string_ref(HRESULT c, wchar_t *perrinfo = nullptr) noexcept
#else
static _base::string_ref _make_string_ref(HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept
#endif
{
_com_error ce(c, perrinfo);
#ifdef _UNICODE
win32::DWORD wlen = (win32::DWORD) wcslen(ce.ErrorMessage());
size_t allocation = wlen + (wlen >> 1);
win32::DWORD bytes;
if(wlen == 0)
{
return _base::string_ref("failed to get message from system");
}
for(;;)
{
auto *p = static_cast<char *>(malloc(allocation)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
}
bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ce.ErrorMessage(), (int) (wlen + 1), p, (int) allocation, nullptr, nullptr);
if(bytes != 0)
{
char *end = strchr(p, 0);
while(end[-1] == 10 || end[-1] == 13)
{
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
}
free(p); // NOLINT
if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/)
{
allocation += allocation >> 2;
continue;
}
return _base::string_ref("failed to get message from system");
}
#else
auto wlen = static_cast<win32::DWORD>(strlen(ce.ErrorMessage()));
auto *p = static_cast<char *>(malloc(wlen + 1)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
}
memcpy(p, ce.ErrorMessage(), wlen + 1);
char *end = strchr(p, 0);
while(end[-1] == 10 || end[-1] == 13)
{
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
#endif
}
public:
//! The value type of the COM code, which is a `HRESULT`
using value_type = HRESULT;
using _base::string_ref;
public:
//! Default constructor
constexpr explicit _com_code_domain(typename _base::unique_id_type id = 0xdc8275428b4effac) noexcept
: _base(id)
{
}
_com_code_domain(const _com_code_domain &) = default;
_com_code_domain(_com_code_domain &&) = default;
_com_code_domain &operator=(const _com_code_domain &) = default;
_com_code_domain &operator=(_com_code_domain &&) = default;
~_com_code_domain() = default;
//! Constexpr singleton getter. Returns the constexpr com_code_domain variable.
static inline constexpr const _com_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("COM domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
return static_cast<const com_code &>(code).value() < 0; // NOLINT
}
/*! Note semantic equivalence testing is only implemented for `FACILITY_WIN32` and `FACILITY_NT_BIT`.
*/
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const com_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const com_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
if((c1.value() & FACILITY_NT_BIT) != 0)
{
if(code2.domain() == nt_code_domain)
{
const auto &c2 = static_cast<const nt_code &>(code2); // NOLINT
if(c2.value() == (c1.value() & ~FACILITY_NT_BIT))
{
return true;
}
}
else if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
if(static_cast<int>(c2.value()) == _nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT))
{
return true;
}
}
}
else if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32)
{
if(code2.domain() == win32_code_domain)
{
const auto &c2 = static_cast<const win32_code &>(code2); // NOLINT
if(c2.value() == HRESULT_CODE(c1.value()))
{
return true;
}
}
else if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
if(static_cast<int>(c2.value()) == _win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value())))
{
return true;
}
}
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c1 = static_cast<const com_code &>(code); // NOLINT
if(c1.value() == S_OK)
{
return generic_code(errc::success);
}
if((c1.value() & FACILITY_NT_BIT) != 0)
{
return generic_code(static_cast<errc>(_nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT)));
}
if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32)
{
return generic_code(static_cast<errc>(_win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value()))));
}
return generic_code(errc::unknown);
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const com_code &>(code); // NOLINT
return _make_string_ref(c.value());
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const com_code &>(code); // NOLINT
throw status_error<_com_code_domain>(c);
}
#endif
};
//! (Windows only) A constexpr source variable for the COM code domain. Returned by `_com_code_domain::get()`.
constexpr _com_code_domain com_code_domain;
inline constexpr const _com_code_domain &_com_code_domain::get()
{
return com_code_domain;
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,459 @@
/* Proposed SG14 status_code
(C) 2018 - 2021 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_CONFIG_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_CONFIG_HPP
// < 0.1 each
#include <cassert>
#include <cstddef> // for size_t
#include <cstdlib> // for free
// 0.22
#include <type_traits>
// 0.29
#include <atomic>
// 0.28 (0.15 of which is exception_ptr)
#include <exception> // for std::exception
// <new> includes <exception>, <exception> includes <new>
#include <new>
// 0.01
#include <initializer_list>
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST
#ifdef __has_include
#if __has_include(<bit>) && (__cplusplus >= 202002L || _HAS_CXX20)
#define BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST 1
#endif
#elif __cplusplus >= 202002L
#define BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST 1
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST
#define BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST 0
#endif
#endif
#if BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST
#include <bit>
#if __cpp_lib_bit_cast < 201806L
#undef BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST
#define BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST 0
#endif
#endif
#if BOOST_OUTCOME_SYSTEM_ERROR2_USE_STD_ADDRESSOF
#include <memory> // for std::addressof
#define BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(...) std::addressof(__VA_ARGS__)
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(...) (&__VA_ARGS__)
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14
#if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
//! Defined to be `constexpr` when on C++ 14 or better compilers. Usually automatic, can be overriden.
#define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 constexpr
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14
#endif
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20
#if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 202000 || _HAS_CXX20
//! Defined to be `constexpr` when on C++ 20 or better compilers. Usually automatic, can be overriden.
#define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 constexpr
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20
#endif
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
#if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN [[noreturn]]
#endif
#endif
#if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN)
#ifdef __has_cpp_attribute
#if __has_cpp_attribute(noreturn)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN [[noreturn]]
#endif
#endif
#endif
#if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN)
#if defined(_MSC_VER)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN __attribute__((__noreturn__))
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
#endif
#endif
// GCCs before 7 don't grok [[noreturn]] virtual functions, and warn annoyingly
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 7
#undef BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
#define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
#if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD [[nodiscard]]
#endif
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
#ifdef __has_cpp_attribute
#if __has_cpp_attribute(nodiscard)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD [[nodiscard]]
#endif
#elif defined(__clang__)
#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD __attribute__((warn_unused_result))
#elif defined(_MSC_VER)
// _Must_inspect_result_ expands into this
#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD \
__declspec( \
"SAL_name" \
"(" \
"\"_Must_inspect_result_\"" \
"," \
"\"\"" \
"," \
"\"2\"" \
")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end")
#endif
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
#define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI
#if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (__clang_major__ >= 7 && !defined(__APPLE__))
//! Defined to be `[[clang::trivial_abi]]` when on a new enough clang compiler. Usually automatic, can be overriden.
#define BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI [[clang::trivial_abi]]
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI
#endif
#endif
#if defined(__cpp_concepts) && !defined(BOOST_OUTCOME_SYSTEM_ERROR2_DISABLE_CONCEPTS_SUPPORT)
#define BOOST_OUTCOME_SYSTEM_ERROR2_GLUE(x, y) x y
#define BOOST_OUTCOME_SYSTEM_ERROR2_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count
#define BOOST_OUTCOME_SYSTEM_ERROR2_EXPAND_ARGS(args) BOOST_OUTCOME_SYSTEM_ERROR2_RETURN_ARG_COUNT args
#define BOOST_OUTCOME_SYSTEM_ERROR2_COUNT_ARGS_MAX8(...) BOOST_OUTCOME_SYSTEM_ERROR2_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO2(name, count) name##count
#define BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO1(name, count) BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO2(name, count)
#define BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO(name, count) BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO1(name, count)
#define BOOST_OUTCOME_SYSTEM_ERROR2_CALL_OVERLOAD(name, ...) BOOST_OUTCOME_SYSTEM_ERROR2_GLUE(BOOST_OUTCOME_SYSTEM_ERROR2_OVERLOAD_MACRO(name, BOOST_OUTCOME_SYSTEM_ERROR2_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__))
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND8(a, b, c, d, e, f, g, h) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND7(b, c, d, e, f, g, h)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND7(a, b, c, d, e, f, g) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND6(b, c, d, e, f, g)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND6(a, b, c, d, e, f) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND5(b, c, d, e, f)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND5(a, b, c, d, e) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND4(b, c, d, e)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND4(a, b, c, d) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND3(b, c, d)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND3(a, b, c) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND2(b, c)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND2(a, b) a &&BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND1(b)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND1(a) a
//! Expands into a && b && c && ...
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(...) requires BOOST_OUTCOME_SYSTEM_ERROR2_CALL_OVERLOAD(BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES_EXPAND, __VA_ARGS__)
#define BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(...) template <__VA_ARGS__>
#define BOOST_OUTCOME_SYSTEM_ERROR2_TEXPR(...) \
requires { (__VA_ARGS__); }
#define BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(...) (__VA_ARGS__)
#if !defined(_MSC_VER) || _MSC_FULL_VER >= 192400000 // VS 2019 16.3 is broken here
#define BOOST_OUTCOME_SYSTEM_ERROR2_REQUIRES(...) requires(__VA_ARGS__)
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_REQUIRES(...)
#endif
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(...) template <__VA_ARGS__
#define BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(...) , __VA_ARGS__ >
#define BOOST_OUTCOME_SYSTEM_ERROR2_TEXPR(...) typename = decltype(__VA_ARGS__)
#ifdef _MSC_VER
// MSVC gives an error if every specialisation of a template is always ill-formed, so
// the more powerful SFINAE form below causes pukeage :(
#define BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(...) typename = typename std::enable_if<(__VA_ARGS__)>::type
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(...) typename std::enable_if<(__VA_ARGS__), bool>::type = true
#endif
#define BOOST_OUTCOME_SYSTEM_ERROR2_REQUIRES(...)
#endif
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE
//! The system_error2 namespace name.
#define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE system_error2
//! Begins the system_error2 namespace.
#define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN \
namespace system_error2 \
{
//! Ends the system_error2 namespace.
#define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END }
#endif
//! Namespace for the library
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
//! Namespace for user specialised traits
namespace traits
{
/*! Specialise to true if you guarantee that a type is move bitcopying (i.e.
its move constructor equals copying bits from old to new, old is left in a
default constructed state, and calling the destructor on a default constructed
instance is trivial). All trivially copyable types are move bitcopying by
definition, and that is the unspecialised implementation.
*/
template <class T> struct is_move_bitcopying
{
static constexpr bool value = std::is_trivially_copyable<T>::value;
};
} // namespace traits
namespace detail
{
#if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
inline constexpr size_t cstrlen(const char *str)
{
const char *end = nullptr;
for(end = str; *end != 0; ++end) // NOLINT
;
return end - str;
}
#else
inline constexpr size_t cstrlen_(const char *str, size_t acc) { return (str[0] == 0) ? acc : cstrlen_(str + 1, acc + 1); }
inline constexpr size_t cstrlen(const char *str) { return cstrlen_(str, 0); }
#endif
#if(__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __cpp_lib_remove_cvref >= 201711L
template <class T> using remove_cvref = std::remove_cvref<T>;
#else
template <class T> struct remove_cvref
{
using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
#endif
/* A partially compliant implementation of C++20's std::bit_cast function contributed
by Jesse Towner.
Our bit_cast is only guaranteed to be constexpr when both the input and output
arguments are either integrals or enums. However, this covers most use cases
since the vast majority of status_codes have an underlying type that is either
an integral or enum. We still attempt a constexpr union-based type pun for non-array
input types, which some compilers accept. For array inputs, we fall back to
non-constexpr memmove.
*/
template <class T> using is_integral_or_enum = std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value>;
template <class To, class From> using is_static_castable = std::integral_constant<bool, is_integral_or_enum<To>::value && is_integral_or_enum<From>::value>;
template <class To, class From>
using is_union_castable = std::integral_constant<bool, !is_static_castable<To, From>::value && !std::is_array<To>::value && !std::is_array<From>::value>;
template <class To, class From>
using is_bit_castable =
std::integral_constant<bool, sizeof(To) == sizeof(From) && traits::is_move_bitcopying<To>::value && traits::is_move_bitcopying<From>::value>;
template <class To, class From> union bit_cast_union
{
From source;
To target;
};
#if BOOST_OUTCOME_SYSTEM_ERROR2_HAVE_BIT_CAST
using std::bit_cast; // available for all trivially copyable types
// For move bit copying types
template <class To, class From>
requires(is_bit_castable<To, From>::value //
&&is_union_castable<To, From>::value //
&& (!std::is_trivially_copyable_v<From> //
|| !std::is_trivially_copyable_v<To>) ) //
constexpr To bit_cast(const From &from) noexcept
{
return bit_cast_union<To, From>{from}.target;
}
template <class To, class From>
requires(is_bit_castable<To, From>::value //
&& !is_union_castable<To, From>::value //
&& (!std::is_trivially_copyable_v<From> //
|| !std::is_trivially_copyable_v<To>) ) //
To bit_cast(const From &from)
noexcept
{
bit_cast_union<To, From> ret;
memmove(&ret.source, &from, sizeof(ret.source));
return ret.target;
}
#else
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, int = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_bit_castable<To, From>::value //
&&is_static_castable<To, From>::value //
&& !is_union_castable<To, From>::value))
constexpr To bit_cast(const From &from) noexcept { return static_cast<To>(from); }
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, long = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_bit_castable<To, From>::value //
&& !is_static_castable<To, From>::value //
&& is_union_castable<To, From>::value))
constexpr To bit_cast(const From &from) noexcept { return bit_cast_union<To, From>{from}.target; }
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_bit_castable<To, From>::value //
&& !is_static_castable<To, From>::value //
&& !is_union_castable<To, From>::value))
To bit_cast(const From &from) noexcept
{
bit_cast_union<To, From> ret;
memmove(&ret.source, &from, sizeof(ret.source));
return ret.target;
}
#endif
/* erasure_cast performs a bit_cast with additional rules to handle types
of differing sizes. For integral & enum types, it may perform a narrowing
or widing conversion with static_cast if necessary, before doing the final
conversion with bit_cast. When casting to or from non-integral, non-enum
types it may insert the value into another object with extra padding bytes
to satisfy bit_cast's preconditions that both types have the same size. */
template <class To, class From>
using is_erasure_castable = std::integral_constant<bool, traits::is_move_bitcopying<To>::value && traits::is_move_bitcopying<From>::value>;
template <class T, bool = std::is_enum<T>::value> struct identity_or_underlying_type
{
using type = T;
};
template <class T> struct identity_or_underlying_type<T, true>
{
using type = typename std::underlying_type<T>::type;
};
template <class OfSize, class OfSign>
using erasure_integer_type = typename std::conditional<std::is_signed<typename identity_or_underlying_type<OfSign>::type>::value,
typename std::make_signed<typename identity_or_underlying_type<OfSize>::type>::type,
typename std::make_unsigned<typename identity_or_underlying_type<OfSize>::type>::type>::type;
template <class ErasedType, std::size_t N> struct padded_erasure_object
{
static_assert(traits::is_move_bitcopying<ErasedType>::value, "ErasedType must be TriviallyCopyable or MoveBitcopying");
static_assert(alignof(ErasedType) <= sizeof(ErasedType), "ErasedType must not be over-aligned");
ErasedType value;
char padding[N];
constexpr explicit padded_erasure_object(const ErasedType &v) noexcept
: value(v)
, padding{}
{
}
};
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value && (sizeof(To) == sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(from); }
#if defined(_WIN32) || defined(__APPLE__) || __LITTLE_ENDIAN__ || (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN)
// We can avoid the type pun on little endian architectures which can aid optimisation
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, long = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value &&is_static_castable<To, From>::value && (sizeof(To) < sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return static_cast<To>(bit_cast<erasure_integer_type<From, To>>(from)); }
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, int = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value &&is_static_castable<To, From>::value && (sizeof(To) > sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(static_cast<erasure_integer_type<To, From>>(from)); }
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) < sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<padded_erasure_object<To, sizeof(From) - sizeof(To)>>(from).value; }
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) > sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(padded_erasure_object<From, sizeof(To) - sizeof(From)>{from}); }
#else
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value && (sizeof(To) < sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<padded_erasure_object<To, sizeof(From) - sizeof(To)>>(from).value; }
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_erasure_castable<To, From>::value && (sizeof(To) > sizeof(From))))
constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(padded_erasure_object<From, sizeof(To) - sizeof(From)>{from}); }
#endif
} // namespace detail
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_FATAL
#ifdef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
#error If BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX is defined, you must define your own BOOST_OUTCOME_SYSTEM_ERROR2_FATAL implementation!
#endif
#include <cstdlib> // for abort
#ifdef __APPLE__
#include <unistd.h> // for write
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
namespace detail
{
namespace avoid_stdio_include
{
#if !defined(__APPLE__) && !defined(_MSC_VER)
extern "C" ptrdiff_t write(int, const void *, size_t);
#elif defined(_MSC_VER)
extern ptrdiff_t write(int, const void *, size_t);
#if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) || (defined(__arm__) || defined(_M_ARM))
#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YA_JHPEBX_K@Z=write")
#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__)
#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YAHHPBXI@Z=_write")
#else
#error Unknown architecture
#endif
#endif
} // namespace avoid_stdio_include
inline void do_fatal_exit(const char *msg)
{
using namespace avoid_stdio_include;
write(2 /*stderr*/, msg, cstrlen(msg));
write(2 /*stderr*/, "\n", 1);
abort();
}
} // namespace detail
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
//! Prints msg to stderr, and calls `std::terminate()`. Can be overriden via predefinition.
#define BOOST_OUTCOME_SYSTEM_ERROR2_FATAL(msg) ::BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::detail::do_fatal_exit(msg)
#endif
#endif
@@ -0,0 +1,98 @@
case 0x80000002: return EACCES;
case 0x8000000f: return EAGAIN;
case 0x80000010: return EAGAIN;
case 0x80000011: return EBUSY;
case 0xc0000002: return ENOSYS;
case 0xc0000005: return EACCES;
case 0xc0000008: return EINVAL;
case 0xc000000e: return ENOENT;
case 0xc000000f: return ENOENT;
case 0xc0000010: return ENOSYS;
case 0xc0000013: return EAGAIN;
case 0xc0000017: return ENOMEM;
case 0xc000001c: return ENOSYS;
case 0xc000001e: return EACCES;
case 0xc000001f: return EACCES;
case 0xc0000021: return EACCES;
case 0xc0000022: return EACCES;
case 0xc0000024: return EINVAL;
case 0xc0000033: return EINVAL;
case 0xc0000034: return ENOENT;
case 0xc0000035: return EEXIST;
case 0xc0000037: return EINVAL;
case 0xc000003a: return ENOENT;
case 0xc0000040: return ENOMEM;
case 0xc0000041: return EACCES;
case 0xc0000042: return EINVAL;
case 0xc0000043: return EACCES;
case 0xc000004b: return EACCES;
case 0xc0000054: return ENOLCK;
case 0xc0000055: return ENOLCK;
case 0xc0000056: return EACCES;
case 0xc000007f: return ENOSPC;
case 0xc0000087: return ENOMEM;
case 0xc0000097: return ENOMEM;
case 0xc000009b: return ENOENT;
case 0xc000009e: return EAGAIN;
case 0xc00000a2: return EACCES;
case 0xc00000a3: return EAGAIN;
case 0xc00000af: return ENOSYS;
case 0xc00000ba: return EACCES;
case 0xc00000c0: return ENODEV;
case 0xc00000d4: return EXDEV;
case 0xc00000d5: return EACCES;
case 0xc00000fb: return ENOENT;
case 0xc0000101: return ENOTEMPTY;
case 0xc0000103: return EINVAL;
case 0xc0000107: return EBUSY;
case 0xc0000108: return EBUSY;
case 0xc000010a: return EACCES;
case 0xc000011f: return EMFILE;
case 0xc0000120: return ECANCELED;
case 0xc0000121: return EACCES;
case 0xc0000123: return EACCES;
case 0xc0000128: return EINVAL;
case 0xc0000189: return EACCES;
case 0xc00001ad: return ENOMEM;
case 0xc000022d: return EAGAIN;
case 0xc0000235: return EINVAL;
case 0xc000026e: return EAGAIN;
case 0xc000028a: return EACCES;
case 0xc000028b: return EACCES;
case 0xc000028d: return EACCES;
case 0xc000028e: return EACCES;
case 0xc000028f: return EACCES;
case 0xc0000290: return EACCES;
case 0xc000029c: return ENOSYS;
case 0xc00002c5: return EACCES;
case 0xc00002d3: return EAGAIN;
case 0xc00002ea: return EACCES;
case 0xc00002f0: return ENOENT;
case 0xc0000373: return ENOMEM;
case 0xc0000416: return ENOMEM;
case 0xc0000433: return EBUSY;
case 0xc0000434: return EBUSY;
case 0xc0000455: return EINVAL;
case 0xc0000467: return EACCES;
case 0xc0000491: return ENOENT;
case 0xc0000495: return EAGAIN;
case 0xc0000503: return EAGAIN;
case 0xc0000507: return EBUSY;
case 0xc0000512: return EACCES;
case 0xc000070a: return EINVAL;
case 0xc000070b: return EINVAL;
case 0xc000070c: return EINVAL;
case 0xc000070d: return EINVAL;
case 0xc000070e: return EINVAL;
case 0xc000070f: return EINVAL;
case 0xc0000710: return ENOSYS;
case 0xc0000711: return ENOSYS;
case 0xc0000716: return EINVAL;
case 0xc000071b: return ENOSYS;
case 0xc000071d: return ENOSYS;
case 0xc000071e: return ENOSYS;
case 0xc000071f: return ENOSYS;
case 0xc0000720: return ENOSYS;
case 0xc0000721: return ENOSYS;
case 0xc000080f: return EAGAIN;
case 0xc000a203: return EACCES;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,75 @@
case 0x1: return ENOSYS;
case 0x2: return ENOENT;
case 0x3: return ENOENT;
case 0x4: return EMFILE;
case 0x5: return EACCES;
case 0x6: return EINVAL;
case 0x8: return ENOMEM;
case 0xc: return EACCES;
case 0xe: return ENOMEM;
case 0xf: return ENODEV;
case 0x10: return EACCES;
case 0x11: return EXDEV;
case 0x13: return EACCES;
case 0x14: return ENODEV;
case 0x15: return EAGAIN;
case 0x19: return EIO;
case 0x1d: return EIO;
case 0x1e: return EIO;
case 0x20: return EACCES;
case 0x21: return ENOLCK;
case 0x27: return ENOSPC;
case 0x37: return ENODEV;
case 0x50: return EEXIST;
case 0x52: return EACCES;
case 0x57: return EINVAL;
case 0x6e: return EIO;
case 0x6f: return ENAMETOOLONG;
case 0x70: return ENOSPC;
case 0x7b: return EINVAL;
case 0x83: return EINVAL;
case 0x8e: return EBUSY;
case 0x91: return ENOTEMPTY;
case 0xaa: return EBUSY;
case 0xb7: return EEXIST;
case 0xd4: return ENOLCK;
case 0x10b: return EINVAL;
case 0x3e3: return ECANCELED;
case 0x3e6: return EACCES;
case 0x3f3: return EIO;
case 0x3f4: return EIO;
case 0x3f5: return EIO;
case 0x4d5: return EAGAIN;
case 0x961: return EBUSY;
case 0x964: return EBUSY;
case 0x2714: return EINTR;
case 0x2719: return EBADF;
case 0x271d: return EACCES;
case 0x271e: return EFAULT;
case 0x2726: return EINVAL;
case 0x2728: return EMFILE;
case 0x2733: return EWOULDBLOCK;
case 0x2734: return EINPROGRESS;
case 0x2735: return EALREADY;
case 0x2736: return ENOTSOCK;
case 0x2737: return EDESTADDRREQ;
case 0x2738: return EMSGSIZE;
case 0x2739: return EPROTOTYPE;
case 0x273a: return ENOPROTOOPT;
case 0x273b: return EPROTONOSUPPORT;
case 0x273d: return EOPNOTSUPP;
case 0x273f: return EAFNOSUPPORT;
case 0x2740: return EADDRINUSE;
case 0x2741: return EADDRNOTAVAIL;
case 0x2742: return ENETDOWN;
case 0x2743: return ENETUNREACH;
case 0x2744: return ENETRESET;
case 0x2745: return ECONNABORTED;
case 0x2746: return ECONNRESET;
case 0x2747: return ENOBUFS;
case 0x2748: return EISCONN;
case 0x2749: return ENOTCONN;
case 0x274c: return ETIMEDOUT;
case 0x274d: return ECONNREFUSED;
case 0x274f: return ENAMETOOLONG;
case 0x2751: return EHOSTUNREACH;
@@ -0,0 +1,65 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_ERROR_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_ERROR_HPP
#include "errored_status_code.hpp"
#include "system_code.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! An errored `system_code` which is always a failure. The closest equivalent to
`std::error_code`, except it cannot be null and cannot be modified.
This refines `system_code` into an `error` object meeting the requirements of
[P0709 Zero-overhead deterministic exceptions](https://wg21.link/P0709).
Differences from `system_code`:
- Always a failure (this is checked at construction, and if not the case,
the program is terminated as this is a logic error)
- No default construction.
- No empty state possible.
- Is immutable.
As with `system_code`, it remains guaranteed to be two CPU registers in size,
and move bitcopying.
*/
using error = erased_errored_status_code<system_code::value_type>;
#ifndef NDEBUG
static_assert(sizeof(error) == 2 * sizeof(void *), "error is not exactly two pointers in size!");
static_assert(traits::is_move_bitcopying<error>::value, "error is not move bitcopying!");
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,438 @@
/* Proposed SG14 status_code
(C) 2018-2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Jun 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_ERRORED_STATUS_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_ERRORED_STATUS_CODE_HPP
#include "quick_status_code_from_enum.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! A `status_code` which is always a failure. The closest equivalent to
`std::error_code`, except it cannot be modified, and is templated.
Differences from `status_code`:
- Never successful (this contract is checked on construction, if fails then it
terminates the process).
- Is immutable.
*/
template <class DomainType> class errored_status_code : public status_code<DomainType>
{
using _base = status_code<DomainType>;
using _base::clear;
using _base::success;
void _check()
{
if(_base::success())
{
std::terminate();
}
}
public:
//! The type of the domain.
using typename _base::domain_type;
//! The type of the error code.
using typename _base::value_type;
//! The type of a reference to a message string.
using typename _base::string_ref;
//! Default constructor.
errored_status_code() = default;
//! Copy constructor.
errored_status_code(const errored_status_code &) = default;
//! Move constructor.
errored_status_code(errored_status_code &&) = default; // NOLINT
//! Copy assignment.
errored_status_code &operator=(const errored_status_code &) = default;
//! Move assignment.
errored_status_code &operator=(errored_status_code &&) = default; // NOLINT
~errored_status_code() = default;
//! Explicitly construct from any similarly erased status code
explicit errored_status_code(const _base &o) noexcept(std::is_nothrow_copy_constructible<_base>::value)
: _base(o)
{
_check();
}
//! Explicitly construct from any similarly erased status code
explicit errored_status_code(_base &&o) noexcept(std::is_nothrow_move_constructible<_base>::value)
: _base(static_cast<_base &&>(o))
{
_check();
}
/***** KEEP THESE IN SYNC WITH 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, errored_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<errored_status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
errored_status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
: errored_status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
{
_check();
}
//! 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<errored_status_code, QuickStatusCodeType>::value)) // Its status code is compatible
errored_status_code(Enum &&v) noexcept(std::is_nothrow_constructible<errored_status_code, QuickStatusCodeType>::value) // NOLINT
: errored_status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
{
_check();
}
//! Explicit in-place construction.
template <class... Args>
explicit errored_status_code(in_place_t _, Args &&...args) noexcept(std::is_nothrow_constructible<value_type, Args &&...>::value)
: _base(_, static_cast<Args &&>(args)...)
{
_check();
}
//! Explicit in-place construction from initialiser list.
template <class T, class... Args>
explicit errored_status_code(in_place_t _, std::initializer_list<T> il,
Args &&...args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<T>, Args &&...>::value)
: _base(_, il, static_cast<Args &&>(args)...)
{
_check();
}
//! Explicit copy construction from a `value_type`.
explicit errored_status_code(const value_type &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: _base(v)
{
_check();
}
//! Explicit move construction from a `value_type`.
explicit errored_status_code(value_type &&v) noexcept(std::is_nothrow_move_constructible<value_type>::value)
: _base(static_cast<value_type &&>(v))
{
_check();
}
/*! 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))
explicit errored_status_code(const status_code<detail::erased<ErasedType>> &v) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
: errored_status_code(detail::erasure_cast<value_type>(v.value())) // NOLINT
{
assert(v.domain() == this->domain()); // NOLINT
_check();
}
//! Always false (including at compile time), as errored status codes are never successful.
constexpr bool success() const noexcept { return false; }
//! Return a const reference to the `value_type`.
constexpr const value_type &value() const &noexcept { return this->_value; }
};
namespace traits
{
template <class DomainType> struct is_move_bitcopying<errored_status_code<DomainType>>
{
static constexpr bool value = is_move_bitcopying<typename DomainType::value_type>::value;
};
} // namespace traits
template <class ErasedType> class errored_status_code<detail::erased<ErasedType>> : public status_code<detail::erased<ErasedType>>
{
using _base = status_code<detail::erased<ErasedType>>;
using _base::success;
void _check()
{
if(_base::success())
{
std::terminate();
}
}
public:
using domain_type = typename _base::domain_type;
using value_type = typename _base::value_type;
using string_ref = typename _base::string_ref;
//! Default construction to empty
errored_status_code() = default;
//! Copy constructor
errored_status_code(const errored_status_code &) = default;
//! Move constructor
errored_status_code(errored_status_code &&) = default; // NOLINT
//! Copy assignment
errored_status_code &operator=(const errored_status_code &) = default;
//! Move assignment
errored_status_code &operator=(errored_status_code &&) = default; // NOLINT
~errored_status_code() = default;
//! Explicitly construct from any similarly erased status code
explicit errored_status_code(const _base &o) noexcept(std::is_nothrow_copy_constructible<_base>::value)
: _base(o)
{
_check();
}
//! Explicitly construct from any similarly erased status code
explicit errored_status_code(_base &&o) noexcept(std::is_nothrow_move_constructible<_base>::value)
: _base(static_cast<_base &&>(o))
{
_check();
}
/***** KEEP THESE IN SYNC WITH 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))
errored_status_code(const status_code<DomainType> &v) noexcept
: _base(v) // NOLINT
{
_check();
}
//! Implicit copy construction from any other status code if its value type is trivially copyable and 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))
errored_status_code(const errored_status_code<DomainType> &v) noexcept
: _base(static_cast<const status_code<DomainType> &>(v)) // NOLINT
{
_check();
}
//! 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))
errored_status_code(status_code<DomainType> &&v) noexcept
: _base(static_cast<status_code<DomainType> &&>(v)) // NOLINT
{
_check();
}
//! 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))
errored_status_code(errored_status_code<DomainType> &&v) noexcept
: _base(static_cast<status_code<DomainType> &&>(v)) // NOLINT
{
_check();
}
//! 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, errored_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<errored_status_code, MakeStatusCodeResult>::value)) // ADLed status code is compatible
errored_status_code(T &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<T>(), std::declval<Args>()...))) // NOLINT
: errored_status_code(make_status_code(static_cast<T &&>(v), static_cast<Args &&>(args)...))
{
_check();
}
//! 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<errored_status_code, QuickStatusCodeType>::value)) // Its status code is compatible
errored_status_code(Enum &&v) noexcept(std::is_nothrow_constructible<errored_status_code, QuickStatusCodeType>::value) // NOLINT
: errored_status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
{
_check();
}
#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 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.
explicit errored_status_code(const status_code<void> &v) // NOLINT
: _base(v)
{
_check();
}
#endif
//! Always false (including at compile time), as errored status codes are never successful.
constexpr bool success() const noexcept { return false; }
//! Return the erased `value_type` by value.
constexpr value_type value() const noexcept { return this->_value; }
};
/*! An erased type specialisation of `errored_status_code<D>`.
Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
*/
template <class ErasedType> using erased_errored_status_code = errored_status_code<detail::erased<ErasedType>>;
namespace traits
{
template <class ErasedType> struct is_move_bitcopying<errored_status_code<detail::erased<ErasedType>>>
{
static constexpr bool value = true;
};
} // namespace traits
//! True if the status code's are semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2>
inline bool operator==(const errored_status_code<DomainType1> &a, const errored_status_code<DomainType2> &b) noexcept
{
return a.equivalent(static_cast<const status_code<DomainType2> &>(b));
}
//! True if the status code's are semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2> inline bool operator==(const status_code<DomainType1> &a, const errored_status_code<DomainType2> &b) noexcept
{
return a.equivalent(static_cast<const status_code<DomainType2> &>(b));
}
//! True if the status code's are semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2> inline bool operator==(const errored_status_code<DomainType1> &a, const status_code<DomainType2> &b) noexcept
{
return static_cast<const status_code<DomainType1> &>(a).equivalent(b);
}
//! True if the status code's are not semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2>
inline bool operator!=(const errored_status_code<DomainType1> &a, const errored_status_code<DomainType2> &b) noexcept
{
return !a.equivalent(static_cast<const status_code<DomainType2> &>(b));
}
//! True if the status code's are not semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2> inline bool operator!=(const status_code<DomainType1> &a, const errored_status_code<DomainType2> &b) noexcept
{
return !a.equivalent(static_cast<const status_code<DomainType2> &>(b));
}
//! True if the status code's are not semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2> inline bool operator!=(const errored_status_code<DomainType1> &a, const status_code<DomainType2> &b) noexcept
{
return !static_cast<const status_code<DomainType1> &>(a).equivalent(b);
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
inline bool operator==(const errored_status_code<DomainType1> &a, const T &b)
{
return a.equivalent(make_status_code(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
inline bool operator==(const T &a, const errored_status_code<DomainType1> &b)
{
return b.equivalent(make_status_code(a));
}
//! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
inline bool operator!=(const errored_status_code<DomainType1> &a, const T &b)
{
return !a.equivalent(make_status_code(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
inline bool operator!=(const T &a, const errored_status_code<DomainType1> &b)
{
return !b.equivalent(make_status_code(a));
}
//! True if the status code's are semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(b)`.
template <class DomainType1, class T, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
inline bool operator==(const errored_status_code<DomainType1> &a, const T &b)
{
return a.equivalent(QuickStatusCodeType(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(a)`.
template <class T, class DomainType1, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
inline bool operator==(const T &a, const errored_status_code<DomainType1> &b)
{
return b.equivalent(QuickStatusCodeType(a));
}
//! True if the status code's are not semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(b)`.
template <class DomainType1, class T, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
inline bool operator!=(const errored_status_code<DomainType1> &a, const T &b)
{
return !a.equivalent(QuickStatusCodeType(b));
}
//! True if the status code's are not semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(a)`.
template <class T, class DomainType1, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
inline bool operator!=(const T &a, const errored_status_code<DomainType1> &b)
{
return !b.equivalent(QuickStatusCodeType(a));
}
namespace detail
{
template <class T> struct is_errored_status_code
{
static constexpr bool value = false;
};
template <class T> struct is_errored_status_code<errored_status_code<T>>
{
static constexpr bool value = true;
};
template <class T> struct is_erased_errored_status_code
{
static constexpr bool value = false;
};
template <class T> struct is_erased_errored_status_code<errored_status_code<erased<T>>>
{
static constexpr bool value = true;
};
} // namespace detail
//! Trait returning true if the type is an errored status code.
template <class T> struct is_errored_status_code
{
static constexpr bool value =
detail::is_errored_status_code<typename std::decay<T>::type>::value || detail::is_erased_errored_status_code<typename std::decay<T>::type>::value;
};
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,491 @@
/* Proposed SG14 status_code
(C) 2018 - 2020 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_GENERIC_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_GENERIC_CODE_HPP
#include "status_error.hpp"
#include <cerrno> // for error constants
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
//! The generic error coding (POSIX)
enum class errc : int
{
success = 0,
unknown = -1,
address_family_not_supported = EAFNOSUPPORT,
address_in_use = EADDRINUSE,
address_not_available = EADDRNOTAVAIL,
already_connected = EISCONN,
argument_list_too_long = E2BIG,
argument_out_of_domain = EDOM,
bad_address = EFAULT,
bad_file_descriptor = EBADF,
bad_message = EBADMSG,
broken_pipe = EPIPE,
connection_aborted = ECONNABORTED,
connection_already_in_progress = EALREADY,
connection_refused = ECONNREFUSED,
connection_reset = ECONNRESET,
cross_device_link = EXDEV,
destination_address_required = EDESTADDRREQ,
device_or_resource_busy = EBUSY,
directory_not_empty = ENOTEMPTY,
executable_format_error = ENOEXEC,
file_exists = EEXIST,
file_too_large = EFBIG,
filename_too_long = ENAMETOOLONG,
function_not_supported = ENOSYS,
host_unreachable = EHOSTUNREACH,
identifier_removed = EIDRM,
illegal_byte_sequence = EILSEQ,
inappropriate_io_control_operation = ENOTTY,
interrupted = EINTR,
invalid_argument = EINVAL,
invalid_seek = ESPIPE,
io_error = EIO,
is_a_directory = EISDIR,
message_size = EMSGSIZE,
network_down = ENETDOWN,
network_reset = ENETRESET,
network_unreachable = ENETUNREACH,
no_buffer_space = ENOBUFS,
no_child_process = ECHILD,
no_link = ENOLINK,
no_lock_available = ENOLCK,
no_message = ENOMSG,
no_protocol_option = ENOPROTOOPT,
no_space_on_device = ENOSPC,
no_stream_resources = ENOSR,
no_such_device_or_address = ENXIO,
no_such_device = ENODEV,
no_such_file_or_directory = ENOENT,
no_such_process = ESRCH,
not_a_directory = ENOTDIR,
not_a_socket = ENOTSOCK,
not_a_stream = ENOSTR,
not_connected = ENOTCONN,
not_enough_memory = ENOMEM,
not_supported = ENOTSUP,
operation_canceled = ECANCELED,
operation_in_progress = EINPROGRESS,
operation_not_permitted = EPERM,
operation_not_supported = EOPNOTSUPP,
operation_would_block = EWOULDBLOCK,
owner_dead = EOWNERDEAD,
permission_denied = EACCES,
protocol_error = EPROTO,
protocol_not_supported = EPROTONOSUPPORT,
read_only_file_system = EROFS,
resource_deadlock_would_occur = EDEADLK,
resource_unavailable_try_again = EAGAIN,
result_out_of_range = ERANGE,
state_not_recoverable = ENOTRECOVERABLE,
stream_timeout = ETIME,
text_file_busy = ETXTBSY,
timed_out = ETIMEDOUT,
too_many_files_open_in_system = ENFILE,
too_many_files_open = EMFILE,
too_many_links = EMLINK,
too_many_symbolic_link_levels = ELOOP,
value_too_large = EOVERFLOW,
wrong_protocol_type = EPROTOTYPE
};
namespace detail
{
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline const char *generic_code_message(errc code) noexcept
{
switch(code)
{
case errc::success:
return "Success";
case errc::address_family_not_supported:
return "Address family not supported by protocol";
case errc::address_in_use:
return "Address already in use";
case errc::address_not_available:
return "Cannot assign requested address";
case errc::already_connected:
return "Transport endpoint is already connected";
case errc::argument_list_too_long:
return "Argument list too long";
case errc::argument_out_of_domain:
return "Numerical argument out of domain";
case errc::bad_address:
return "Bad address";
case errc::bad_file_descriptor:
return "Bad file descriptor";
case errc::bad_message:
return "Bad message";
case errc::broken_pipe:
return "Broken pipe";
case errc::connection_aborted:
return "Software caused connection abort";
case errc::connection_already_in_progress:
return "Operation already in progress";
case errc::connection_refused:
return "Connection refused";
case errc::connection_reset:
return "Connection reset by peer";
case errc::cross_device_link:
return "Invalid cross-device link";
case errc::destination_address_required:
return "Destination address required";
case errc::device_or_resource_busy:
return "Device or resource busy";
case errc::directory_not_empty:
return "Directory not empty";
case errc::executable_format_error:
return "Exec format error";
case errc::file_exists:
return "File exists";
case errc::file_too_large:
return "File too large";
case errc::filename_too_long:
return "File name too long";
case errc::function_not_supported:
return "Function not implemented";
case errc::host_unreachable:
return "No route to host";
case errc::identifier_removed:
return "Identifier removed";
case errc::illegal_byte_sequence:
return "Invalid or incomplete multibyte or wide character";
case errc::inappropriate_io_control_operation:
return "Inappropriate ioctl for device";
case errc::interrupted:
return "Interrupted system call";
case errc::invalid_argument:
return "Invalid argument";
case errc::invalid_seek:
return "Illegal seek";
case errc::io_error:
return "Input/output error";
case errc::is_a_directory:
return "Is a directory";
case errc::message_size:
return "Message too long";
case errc::network_down:
return "Network is down";
case errc::network_reset:
return "Network dropped connection on reset";
case errc::network_unreachable:
return "Network is unreachable";
case errc::no_buffer_space:
return "No buffer space available";
case errc::no_child_process:
return "No child processes";
case errc::no_link:
return "Link has been severed";
case errc::no_lock_available:
return "No locks available";
case errc::no_message:
return "No message of desired type";
case errc::no_protocol_option:
return "Protocol not available";
case errc::no_space_on_device:
return "No space left on device";
case errc::no_stream_resources:
return "Out of streams resources";
case errc::no_such_device_or_address:
return "No such device or address";
case errc::no_such_device:
return "No such device";
case errc::no_such_file_or_directory:
return "No such file or directory";
case errc::no_such_process:
return "No such process";
case errc::not_a_directory:
return "Not a directory";
case errc::not_a_socket:
return "Socket operation on non-socket";
case errc::not_a_stream:
return "Device not a stream";
case errc::not_connected:
return "Transport endpoint is not connected";
case errc::not_enough_memory:
return "Cannot allocate memory";
#if ENOTSUP != EOPNOTSUPP
case errc::not_supported:
return "Operation not supported";
#endif
case errc::operation_canceled:
return "Operation canceled";
case errc::operation_in_progress:
return "Operation now in progress";
case errc::operation_not_permitted:
return "Operation not permitted";
case errc::operation_not_supported:
return "Operation not supported";
#if EAGAIN != EWOULDBLOCK
case errc::operation_would_block:
return "Resource temporarily unavailable";
#endif
case errc::owner_dead:
return "Owner died";
case errc::permission_denied:
return "Permission denied";
case errc::protocol_error:
return "Protocol error";
case errc::protocol_not_supported:
return "Protocol not supported";
case errc::read_only_file_system:
return "Read-only file system";
case errc::resource_deadlock_would_occur:
return "Resource deadlock avoided";
case errc::resource_unavailable_try_again:
return "Resource temporarily unavailable";
case errc::result_out_of_range:
return "Numerical result out of range";
case errc::state_not_recoverable:
return "State not recoverable";
case errc::stream_timeout:
return "Timer expired";
case errc::text_file_busy:
return "Text file busy";
case errc::timed_out:
return "Connection timed out";
case errc::too_many_files_open_in_system:
return "Too many open files in system";
case errc::too_many_files_open:
return "Too many open files";
case errc::too_many_links:
return "Too many links";
case errc::too_many_symbolic_link_levels:
return "Too many levels of symbolic links";
case errc::value_too_large:
return "Value too large for defined data type";
case errc::wrong_protocol_type:
return "Protocol wrong type for socket";
default:
return "unknown";
}
}
} // namespace detail
/*! The implementation of the domain for generic status codes, those mapped by `errc` (POSIX).
*/
class _generic_code_domain : public status_code_domain
{
template <class> friend class status_code;
using _base = status_code_domain;
public:
//! The value type of the generic code, which is an `errc` as per POSIX.
using value_type = errc;
using string_ref = _base::string_ref;
public:
//! Default constructor
constexpr explicit _generic_code_domain(typename _base::unique_id_type id = 0x746d6354f4f733e9) noexcept
: _base(id)
{
}
_generic_code_domain(const _generic_code_domain &) = default;
_generic_code_domain(_generic_code_domain &&) = default;
_generic_code_domain &operator=(const _generic_code_domain &) = default;
_generic_code_domain &operator=(_generic_code_domain &&) = default;
~_generic_code_domain() = default;
//! Constexpr singleton getter. Returns the constexpr generic_code_domain variable.
static inline constexpr const _generic_code_domain &get();
virtual _base::string_ref name() const noexcept override { return string_ref("generic domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const generic_code &>(code).value() != errc::success; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const generic_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const generic_code &>(code); // NOLINT
}
virtual _base::string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const generic_code &>(code); // NOLINT
return string_ref(detail::generic_code_message(c.value()));
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const generic_code &>(code); // NOLINT
throw status_error<_generic_code_domain>(c);
}
#endif
};
//! A specialisation of `status_error` for the generic code domain.
using generic_error = status_error<_generic_code_domain>;
//! A constexpr source variable for the generic code domain, which is that of `errc` (POSIX). Returned by `_generic_code_domain::get()`.
constexpr _generic_code_domain generic_code_domain;
inline constexpr const _generic_code_domain &_generic_code_domain::get()
{
return generic_code_domain;
}
// Enable implicit construction of generic_code from errc
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline generic_code make_status_code(errc c) noexcept
{
return generic_code(in_place, c);
}
/*************************************************************************************************************/
template <class T> inline BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 bool status_code<void>::equivalent(const status_code<T> &o) const noexcept
{
if(_domain && o._domain)
{
if(_domain->_do_equivalent(*this, o))
{
return true;
}
if(o._domain->_do_equivalent(o, *this))
{
return true;
}
generic_code c1 = o._domain->_generic_code(o);
if(c1.value() != errc::unknown && _domain->_do_equivalent(*this, c1))
{
return true;
}
generic_code c2 = _domain->_generic_code(*this);
if(c2.value() != errc::unknown && o._domain->_do_equivalent(o, c2))
{
return true;
}
}
// If we are both empty, we are equivalent, otherwise not equivalent
return (!_domain && !o._domain);
}
//! True if the status code's are semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code<DomainType1> &a, const status_code<DomainType2> &b) noexcept
{
return a.equivalent(b);
}
//! True if the status code's are not semantically equal via `equivalent()`.
template <class DomainType1, class DomainType2>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code<DomainType1> &a, const status_code<DomainType2> &b) noexcept
{
return !a.equivalent(b);
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code<DomainType1> &a, const T &b)
{
return a.equivalent(make_status_code(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code<DomainType1> &b)
{
return b.equivalent(make_status_code(a));
}
//! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code<DomainType1> &a, const T &b)
{
return !a.equivalent(make_status_code(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
class MakeStatusCodeResult =
typename detail::safe_get_make_status_code_result<const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code<DomainType1> &b)
{
return !b.equivalent(make_status_code(a));
}
//! True if the status code's are semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(b)`.
template <class DomainType1, class T, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code<DomainType1> &a, const T &b)
{
return a.equivalent(QuickStatusCodeType(b));
}
//! True if the status code's are semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(a)`.
template <class T, class DomainType1, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code<DomainType1> &b)
{
return b.equivalent(QuickStatusCodeType(a));
}
//! True if the status code's are not semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(b)`.
template <class DomainType1, class T, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code<DomainType1> &a, const T &b)
{
return !a.equivalent(QuickStatusCodeType(b));
}
//! True if the status code's are not semantically equal via `equivalent()` to `quick_status_code_from_enum<T>::code_type(a)`.
template <class T, class DomainType1, //
class QuickStatusCodeType = typename quick_status_code_from_enum<T>::code_type // Enumeration has been activated
>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code<DomainType1> &b)
{
return !b.equivalent(QuickStatusCodeType(a));
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,164 @@
/* Proposed SG14 status_code
(C) 2020-2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Jan 2020
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_GETADDRINFO_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_GETADDRINFO_CODE_HPP
#include "quick_status_code_from_enum.hpp"
#ifdef _WIN32
#error Not available for Microsoft Windows
#else
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
class _getaddrinfo_code_domain;
//! A getaddrinfo error code, those returned by `getaddrinfo()`.
using getaddrinfo_code = status_code<_getaddrinfo_code_domain>;
//! A specialisation of `status_error` for the `getaddrinfo()` error code domain.
using getaddrinfo_error = status_error<_getaddrinfo_code_domain>;
/*! The implementation of the domain for `getaddrinfo()` error codes, those returned by `getaddrinfo()`.
*/
class _getaddrinfo_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
public:
//! The value type of the `getaddrinfo()` code, which is an `int`
using value_type = int;
using _base::string_ref;
//! Default constructor
constexpr explicit _getaddrinfo_code_domain(typename _base::unique_id_type id = 0x5b24b2de470ff7b6) noexcept
: _base(id)
{
}
_getaddrinfo_code_domain(const _getaddrinfo_code_domain &) = default;
_getaddrinfo_code_domain(_getaddrinfo_code_domain &&) = default;
_getaddrinfo_code_domain &operator=(const _getaddrinfo_code_domain &) = default;
_getaddrinfo_code_domain &operator=(_getaddrinfo_code_domain &&) = default;
~_getaddrinfo_code_domain() = default;
//! Constexpr singleton getter. Returns constexpr getaddrinfo_code_domain variable.
static inline constexpr const _getaddrinfo_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("getaddrinfo() domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const getaddrinfo_code &>(code).value() != 0; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const getaddrinfo_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const getaddrinfo_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const getaddrinfo_code &>(code); // NOLINT
switch(c.value())
{
#ifdef EAI_ADDRFAMILY
case EAI_ADDRFAMILY:
return errc::no_such_device_or_address;
#endif
case EAI_FAIL:
return errc::io_error;
case EAI_MEMORY:
return errc::not_enough_memory;
#ifdef EAI_NODATA
case EAI_NODATA:
return errc::no_such_device_or_address;
#endif
case EAI_NONAME:
return errc::no_such_device_or_address;
#ifdef EAI_OVERFLOW
case EAI_OVERFLOW:
return errc::argument_list_too_long;
#endif
case EAI_BADFLAGS: // fallthrough
case EAI_SERVICE:
return errc::invalid_argument;
case EAI_FAMILY: // fallthrough
case EAI_SOCKTYPE:
return errc::operation_not_supported;
case EAI_AGAIN: // fallthrough
case EAI_SYSTEM:
return errc::resource_unavailable_try_again;
default:
return errc::unknown;
}
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const getaddrinfo_code &>(code); // NOLINT
return string_ref(gai_strerror(c.value()));
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const getaddrinfo_code &>(code); // NOLINT
throw status_error<_getaddrinfo_code_domain>(c);
}
#endif
};
//! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by `_getaddrinfo_code_domain::get()`.
constexpr _getaddrinfo_code_domain getaddrinfo_code_domain;
inline constexpr const _getaddrinfo_code_domain &_getaddrinfo_code_domain::get()
{
return getaddrinfo_code_domain;
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,339 @@
/* Proposed SG14 status_code
(C) 2022-2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Jun 2022
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_HTTP_STATUS_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_HTTP_STATUS_CODE_HPP
#include "status_code.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
class _http_status_code_domain;
//! A HTTP status code.
using http_status_code = status_code<_http_status_code_domain>;
namespace mixins
{
template <class Base> struct mixin<Base, _http_status_code_domain> : public Base
{
using Base::Base;
//! True if the HTTP status code is informational
inline bool is_http_informational() const noexcept;
//! True if the HTTP status code is successful
inline bool is_http_success() const noexcept;
//! True if the HTTP status code is redirection
inline bool is_http_redirection() const noexcept;
//! True if the HTTP status code is client error
inline bool is_http_client_error() const noexcept;
//! True if the HTTP status code is server error
inline bool is_http_server_error() const noexcept;
};
} // namespace mixins
/*! The implementation of the domain for HTTP status codes.
*/
class _http_status_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
public:
//! The value type of the HTTP code, which is an `int`
using value_type = int;
using _base::string_ref;
//! Default constructor
constexpr explicit _http_status_code_domain(typename _base::unique_id_type id = 0xbdb4cde88378a333ull) noexcept
: _base(id)
{
}
_http_status_code_domain(const _http_status_code_domain &) = default;
_http_status_code_domain(_http_status_code_domain &&) = default;
_http_status_code_domain &operator=(const _http_status_code_domain &) = default;
_http_status_code_domain &operator=(_http_status_code_domain &&) = default;
~_http_status_code_domain() = default;
//! Constexpr singleton getter. Returns constexpr http_status_code_domain variable.
static inline constexpr const _http_status_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("HTTP status domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const http_status_code &>(code).value() >= 400; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const http_status_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const http_status_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const http_status_code &>(code); // NOLINT
switch(c.value())
{
case 102:
case 202:
return errc::operation_in_progress;
case 400:
return errc::invalid_argument;
case 401:
return errc::operation_not_permitted;
case 403:
return errc::permission_denied;
case 404:
case 410:
return errc::no_such_file_or_directory;
case 405:
case 418:
return errc::operation_not_supported;
case 406:
return errc::protocol_not_supported;
case 408:
return errc::timed_out;
case 413:
return errc::result_out_of_range;
case 501:
return errc::not_supported;
case 503:
return errc::resource_unavailable_try_again;
case 504:
return errc::timed_out;
case 507:
return errc::no_space_on_device;
default:
return errc::unknown;
}
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const http_status_code &>(code); // NOLINT
return string_ref(
[&]() -> const char *
{
switch(c.value())
{
case 100:
return "Continue";
case 101:
return "Switching Protocols";
case 102:
return "Processing";
case 103:
return "Early Hints";
case 200:
return "OK";
case 201:
return "Created";
case 202:
return "Accepted";
case 203:
return "Non-Authoritative Information";
case 204:
return "No Content";
case 205:
return "Reset Content";
case 206:
return "Partial Content";
case 207:
return "Multi-Status";
case 208:
return "Already Reported";
case 209:
return "IM Used";
case 300:
return "Multiple Choices";
case 301:
return "Moved Permanently";
case 302:
return "Found";
case 303:
return "See Other";
case 304:
return "Not Modified";
case 305:
return "Use Proxy";
case 306:
return "Switch Proxy";
case 307:
return "Temporary Redirect";
case 308:
return "Permanent Redirect";
case 400:
return "Bad Request";
case 401:
return "Unauthorized";
case 402:
return "Payment Required";
case 403:
return "Forbidden";
case 404:
return "Not Found";
case 405:
return "Method Not Allowed";
case 406:
return "Not Acceptable";
case 407:
return "Proxy Authentication Required";
case 408:
return "Request Timeout";
case 409:
return "Conflict";
case 410:
return "Gone";
case 411:
return "Length Required";
case 412:
return "Precondition Failed";
case 413:
return "Payload Too Large";
case 414:
return "URI Too Long";
case 415:
return "Unsupported Media Type";
case 416:
return "Range Not Satisfiable";
case 417:
return "Expectation Failed";
case 418:
return "I'm a teapot";
case 421:
return "Misdirected Request";
case 422:
return "Unprocessable Entity";
case 423:
return "Locked";
case 424:
return "Failed Dependency";
case 425:
return "Too Early";
case 426:
return "Upgrade Required";
case 428:
return "Precondition Required";
case 429:
return "Too Many Requests";
case 431:
return "Request Header Fields Too Large";
case 451:
return "Unavailable For Legal Reasons";
case 500:
return "Internal Server Error";
case 501:
return "Not Implemented";
case 502:
return "Bad Gateway";
case 503:
return "Service Unavailable";
case 504:
return "Gateway Timeout";
case 505:
return "HTTP Version Not Supported";
case 506:
return "Variant Also Negotiates";
case 507:
return "Insufficient Storage";
case 508:
return "Loop Detected";
case 510:
return "Not Extended";
case 511:
return "Network Authentication Required";
default:
return "Unknown";
}
}());
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const http_status_code &>(code); // NOLINT
throw status_error<_http_status_code_domain>(c);
}
#endif
};
//! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by `_http_status_code_domain::get()`.
constexpr _http_status_code_domain http_status_code_domain;
inline constexpr const _http_status_code_domain &_http_status_code_domain::get()
{
return http_status_code_domain;
}
namespace mixins
{
template <class Base> inline bool mixin<Base, _http_status_code_domain>::is_http_informational() const noexcept
{
const auto &c = static_cast<const http_status_code *>(this)->value();
return c >= 100 && c < 200;
}
template <class Base> inline bool mixin<Base, _http_status_code_domain>::is_http_success() const noexcept
{
const auto &c = static_cast<const http_status_code *>(this)->value();
return c >= 200 && c < 300;
}
template <class Base> inline bool mixin<Base, _http_status_code_domain>::is_http_redirection() const noexcept
{
const auto &c = static_cast<const http_status_code *>(this)->value();
return c >= 300 && c < 400;
}
template <class Base> inline bool mixin<Base, _http_status_code_domain>::is_http_client_error() const noexcept
{
const auto &c = static_cast<const http_status_code *>(this)->value();
return c >= 400 && c < 500;
}
template <class Base> inline bool mixin<Base, _http_status_code_domain>::is_http_server_error() const noexcept
{
const auto &c = static_cast<const http_status_code *>(this)->value();
return c >= 500 && c < 600;
}
} // namespace mixins
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,87 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_IOSTREAM_SUPPORT_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_IOSTREAM_SUPPORT_HPP
#include "error.hpp"
#include <ostream>
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! Print the status code to a `std::ostream &`.
Requires that `DomainType::value_type` implements an `operator<<` overload for `std::ostream`.
*/
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class DomainType) //
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(
std::is_same<std::ostream,
typename std::decay<decltype(std::declval<std::ostream>() << std::declval<typename status_code<DomainType>::value_type>())>::type>::value))
inline std::ostream &operator<<(std::ostream &s, const status_code<DomainType> &v)
{
if(v.empty())
{
return s << "(empty)";
}
return s << v.domain().name().c_str() << ": " << v.value();
}
/*! Print a status code domain's `string_ref` to a `std::ostream &`.
*/
inline std::ostream &operator<<(std::ostream &s, const status_code_domain::string_ref &v)
{
return s << v.c_str();
}
/*! Print the erased status code to a `std::ostream &`.
*/
template <class ErasedType> inline std::ostream &operator<<(std::ostream &s, const status_code<detail::erased<ErasedType>> &v)
{
if(v.empty())
{
return s << "(empty)";
}
return s << v.domain().name() << ": " << v.message();
}
/*! Print the generic code to a `std::ostream &`.
*/
inline std::ostream &operator<<(std::ostream &s, const generic_code &v)
{
if(v.empty())
{
return s << "(empty)";
}
return s << v.domain().name() << ": " << v.message();
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,256 @@
/* Pointer to a SG14 status_code
(C) 2018 - 2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Sep 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_NESTED_STATUS_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP
#include "quick_status_code_from_enum.hpp"
#include <memory> // for allocator
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
namespace detail
{
template <class StatusCode, class Allocator> class indirecting_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
public:
struct payload_type
{
using allocator_traits = std::allocator_traits<Allocator>;
union
{
char _uninit[sizeof(StatusCode)];
StatusCode sc;
};
Allocator alloc;
payload_type(StatusCode _sc, Allocator _alloc)
: alloc(static_cast<Allocator &&>(_alloc))
{
allocator_traits::construct(alloc, &sc, static_cast<StatusCode &&>(_sc));
}
payload_type(const payload_type &) = delete;
payload_type(payload_type &&) = delete;
~payload_type() { allocator_traits::destroy(alloc, &sc); }
};
using value_type = payload_type *;
using payload_allocator_traits = typename payload_type::allocator_traits::template rebind_traits<payload_type>;
using _base::string_ref;
constexpr indirecting_domain() noexcept
: _base(0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id() /* unique-ish based on domain's unique id */)
{
}
indirecting_domain(const indirecting_domain &) = default;
indirecting_domain(indirecting_domain &&) = default; // NOLINT
indirecting_domain &operator=(const indirecting_domain &) = default;
indirecting_domain &operator=(indirecting_domain &&) = default; // NOLINT
~indirecting_domain() = default;
#if __cplusplus < 201402L && !defined(_MSC_VER)
static inline const indirecting_domain &get()
{
static indirecting_domain v;
return v;
}
#else
static inline constexpr const indirecting_domain &get();
#endif
virtual string_ref name() const noexcept override { return typename StatusCode::domain_type().name(); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
using _mycode = status_code<indirecting_domain>;
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const _mycode &>(code); // NOLINT
return static_cast<status_code_domain &&>(typename StatusCode::domain_type())._do_failure(c.value()->sc);
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const _mycode &>(code1); // NOLINT
return static_cast<status_code_domain &&>(typename StatusCode::domain_type())._do_equivalent(c1.value()->sc, code2);
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const _mycode &>(code); // NOLINT
return static_cast<status_code_domain &&>(typename StatusCode::domain_type())._generic_code(c.value()->sc);
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const _mycode &>(code); // NOLINT
return static_cast<status_code_domain &&>(typename StatusCode::domain_type())._do_message(c.value()->sc);
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const _mycode &>(code); // NOLINT
static_cast<status_code_domain &&>(typename StatusCode::domain_type())._do_throw_exception(c.value()->sc);
abort(); // suppress buggy GCC warning
}
#endif
virtual bool _do_erased_copy(status_code<void> &dst, const status_code<void> &src, payload_info_t dstinfo) const override // NOLINT
{
// Note that dst may not have its domain set
const auto srcinfo = payload_info();
assert(src.domain() == *this);
if(dstinfo.total_size < srcinfo.total_size)
{
return false;
}
auto &d = static_cast<_mycode &>(dst); // NOLINT
const auto &_s = static_cast<const _mycode &>(src); // NOLINT
const payload_type &sp = *_s.value();
typename payload_allocator_traits::template rebind_alloc<payload_type> payload_alloc(sp.alloc);
auto *dp = payload_allocator_traits::allocate(payload_alloc, 1);
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
try
#endif
{
payload_allocator_traits::construct(payload_alloc, dp, sp.sc, sp.alloc);
new(BOOST_OUTCOME_SYSTEM_ERROR2_ADDRESS_OF(d)) _mycode(in_place, dp);
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
catch(...)
{
payload_allocator_traits::deallocate(payload_alloc, dp, 1);
throw;
}
#endif
return true;
}
virtual void _do_erased_destroy(status_code<void> &code, payload_info_t /*unused*/) const noexcept override // NOLINT
{
assert(code.domain() == *this);
auto &c = static_cast<_mycode &>(code); // NOLINT
payload_type *p = c.value();
typename payload_allocator_traits::template rebind_alloc<payload_type> payload_alloc(p->alloc);
payload_allocator_traits::destroy(payload_alloc, p);
payload_allocator_traits::deallocate(payload_alloc, p, 1);
}
};
#if __cplusplus >= 201402L || defined(_MSC_VER)
template <class StatusCode, class Allocator> constexpr indirecting_domain<StatusCode, Allocator> _indirecting_domain{};
template <class StatusCode, class Allocator>
inline constexpr const indirecting_domain<StatusCode, Allocator> &indirecting_domain<StatusCode, Allocator>::get()
{
return _indirecting_domain<StatusCode, Allocator>;
}
#endif
} // namespace detail
/*! Make an erased status code which indirects to a dynamically allocated status code,
using the allocator `alloc`.
This is useful for shoehorning a rich status code with large value type into a small
erased status code like `system_code`, with which the status code generated by this
function is compatible. Note that this function can throw if the allocator throws.
*/
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class T, class Alloc = std::allocator<typename std::decay<T>::type>)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<T>::value)) //
inline status_code<detail::erased<typename std::add_pointer<typename std::decay<T>::type>::type>> make_nested_status_code(T &&v, Alloc alloc = {})
{
using status_code_type = typename std::decay<T>::type;
using domain_type = detail::indirecting_domain<status_code_type, typename std::decay<Alloc>::type>;
using payload_allocator_traits = typename domain_type::payload_allocator_traits;
typename payload_allocator_traits::template rebind_alloc<typename domain_type::payload_type> payload_alloc(alloc);
auto *p = payload_allocator_traits::allocate(payload_alloc, 1);
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
try
#endif
{
payload_allocator_traits::construct(payload_alloc, p, static_cast<T &&>(v), static_cast<Alloc &&>(alloc));
return status_code<domain_type>(in_place, p);
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
catch(...)
{
payload_allocator_traits::deallocate(payload_alloc, p, 1);
throw;
}
#endif
}
/*! If a status code refers to a `nested_status_code` which indirects to a status
code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null.
*/
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<StatusCode>::value)) inline StatusCode *get_if(status_code<detail::erased<U>> *v) noexcept
{
if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id())
{
return nullptr;
}
union
{
U value;
StatusCode *ret;
};
value = v->value();
return ret;
}
//! \overload Const overload
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(is_status_code<StatusCode>::value))
inline const StatusCode *get_if(const status_code<detail::erased<U>> *v) noexcept
{
if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id())
{
return nullptr;
}
union
{
U value;
const StatusCode *ret;
};
value = v->value();
return ret;
}
/*! If a status code refers to a `nested_status_code`, return the id of the erased
status code's domain. Otherwise return a meaningless number.
*/
template <class U> inline typename status_code_domain::unique_id_type get_id(const status_code<detail::erased<U>> &v) noexcept
{
return 0xc44f7bdeb2cc50e9 ^ v.domain().id();
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,233 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_NT_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_NT_CODE_HPP
#if !defined(_WIN32) && !defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#error This file should only be included on Windows
#endif
#include "win32_code.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
//! \exclude
namespace win32
{
// A Win32 NTSTATUS
using NTSTATUS = long;
// A Win32 HMODULE
using HMODULE = void *;
// Used to retrieve where the NTDLL DLL is mapped into memory
extern HMODULE __stdcall GetModuleHandleW(const wchar_t *lpModuleName);
#pragma comment(lib, "kernel32.lib")
#if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64))
#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPEAXPEB_W@Z=GetModuleHandleW")
#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__)
#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YGPAXPB_W@Z=_GetModuleHandleW@4")
#elif defined(__arm__) || defined(_M_ARM)
#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPAXPB_W@Z=GetModuleHandleW")
#else
#error Unknown architecture
#endif
} // namespace win32
class _nt_code_domain;
//! (Windows only) A NT error code, those returned by NT kernel functions.
using nt_code = status_code<_nt_code_domain>;
//! (Windows only) A specialisation of `status_error` for the NT error code domain.
using nt_error = status_error<_nt_code_domain>;
/*! (Windows only) The implementation of the domain for NT error codes, those returned by NT kernel functions.
*/
class _nt_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
friend class _com_code_domain;
using _base = status_code_domain;
static int _nt_code_to_errno(win32::NTSTATUS c)
{
if(c >= 0)
{
return 0; // success
}
switch(static_cast<unsigned>(c))
{
#include "detail/nt_code_to_generic_code.ipp"
}
return -1;
}
static win32::DWORD _nt_code_to_win32_code(win32::NTSTATUS c) // NOLINT
{
if(c >= 0)
{
return 0; // success
}
switch(static_cast<unsigned>(c))
{
#include "detail/nt_code_to_win32_code.ipp"
}
return static_cast<win32::DWORD>(-1);
}
//! Construct from a NT error code
static _base::string_ref _make_string_ref(win32::NTSTATUS c) noexcept
{
wchar_t buffer[32768];
static win32::HMODULE ntdll = win32::GetModuleHandleW(L"NTDLL.DLL");
win32::DWORD wlen =
win32::FormatMessageW(0x00000800 /*FORMAT_MESSAGE_FROM_HMODULE*/ | 0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/,
ntdll, c, (1 << 10) /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, buffer, 32768, nullptr);
size_t allocation = wlen + (wlen >> 1);
win32::DWORD bytes;
if(wlen == 0)
{
return _base::string_ref("failed to get message from system");
}
for(;;)
{
auto *p = static_cast<char *>(malloc(allocation)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
}
bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr);
if(bytes != 0)
{
char *end = strchr(p, 0);
while(end[-1] == 10 || end[-1] == 13)
{
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
}
free(p); // NOLINT
if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/)
{
allocation += allocation >> 2;
continue;
}
return _base::string_ref("failed to get message from system");
}
}
public:
//! The value type of the NT code, which is a `win32::NTSTATUS`
using value_type = win32::NTSTATUS;
using _base::string_ref;
public:
//! Default constructor
constexpr explicit _nt_code_domain(typename _base::unique_id_type id = 0x93f3b4487e4af25b) noexcept
: _base(id)
{
}
_nt_code_domain(const _nt_code_domain &) = default;
_nt_code_domain(_nt_code_domain &&) = default;
_nt_code_domain &operator=(const _nt_code_domain &) = default;
_nt_code_domain &operator=(_nt_code_domain &&) = default;
~_nt_code_domain() = default;
//! Constexpr singleton getter. Returns the constexpr nt_code_domain variable.
static inline constexpr const _nt_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("NT domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
return static_cast<const nt_code &>(code).value() < 0; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const nt_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const nt_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
if(static_cast<int>(c2.value()) == _nt_code_to_errno(c1.value()))
{
return true;
}
}
if(code2.domain() == win32_code_domain)
{
const auto &c2 = static_cast<const win32_code &>(code2); // NOLINT
if(c2.value() == _nt_code_to_win32_code(c1.value()))
{
return true;
}
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const nt_code &>(code); // NOLINT
return generic_code(static_cast<errc>(_nt_code_to_errno(c.value())));
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const nt_code &>(code); // NOLINT
return _make_string_ref(c.value());
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const nt_code &>(code); // NOLINT
throw status_error<_nt_code_domain>(c);
}
#endif
};
//! (Windows only) A constexpr source variable for the NT code domain, which is that of NT kernel functions. Returned by `_nt_code_domain::get()`.
constexpr _nt_code_domain nt_code_domain;
inline constexpr const _nt_code_domain &_nt_code_domain::get()
{
return nt_code_domain;
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,201 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_POSIX_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_POSIX_CODE_HPP
#ifdef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
#error <posix_code.hpp> is not includable when BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX is defined!
#endif
#include "quick_status_code_from_enum.hpp"
#include <cstring> // for strchr and strerror_r
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
// Fix for issue #48 Issue compiling on arm-none-eabi (newlib) with GNU extensions off
#if !defined(_MSC_VER) && !defined(__APPLE__)
namespace detail
{
namespace avoid_string_include
{
#if defined(__GLIBC__) && !defined(__UCLIBC__)
// This returns int for non-glibc strerror_r, but glibc's is particularly weird so we retain it
extern "C" char *strerror_r(int errnum, char *buf, size_t buflen);
#else
extern "C" int strerror_r(int errnum, char *buf, size_t buflen);
#endif
} // namespace avoid_string_include
} // namespace detail
#endif
class _posix_code_domain;
//! A POSIX error code, those returned by `errno`.
using posix_code = status_code<_posix_code_domain>;
//! A specialisation of `status_error` for the POSIX error code domain.
using posix_error = status_error<_posix_code_domain>;
namespace mixins
{
template <class Base> struct mixin<Base, _posix_code_domain> : public Base
{
using Base::Base;
//! Returns a `posix_code` for the current value of `errno`.
static posix_code current() noexcept;
};
} // namespace mixins
/*! The implementation of the domain for POSIX error codes, those returned by `errno`.
*/
class _posix_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
static _base::string_ref _make_string_ref(int c) noexcept
{
char buffer[1024] = "";
#ifdef _WIN32
strerror_s(buffer, sizeof(buffer), c);
#elif defined(__GLIBC__) && !defined(__UCLIBC__) // handle glibc's weird strerror_r()
char *s = detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); // NOLINT
if(s != nullptr)
{
strncpy(buffer, s, sizeof(buffer) - 1); // NOLINT
buffer[1023] = 0;
}
#elif !defined(__APPLE__)
detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer));
#else
strerror_r(c, buffer, sizeof(buffer));
#endif
size_t length = strlen(buffer); // NOLINT
auto *p = static_cast<char *>(malloc(length + 1)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
}
memcpy(p, buffer, length + 1); // NOLINT
return _base::atomic_refcounted_string_ref(p, length);
}
public:
//! The value type of the POSIX code, which is an `int`
using value_type = int;
using _base::string_ref;
//! Default constructor
constexpr explicit _posix_code_domain(typename _base::unique_id_type id = 0xa59a56fe5f310933) noexcept
: _base(id)
{
}
_posix_code_domain(const _posix_code_domain &) = default;
_posix_code_domain(_posix_code_domain &&) = default;
_posix_code_domain &operator=(const _posix_code_domain &) = default;
_posix_code_domain &operator=(_posix_code_domain &&) = default;
~_posix_code_domain() = default;
//! Constexpr singleton getter. Returns constexpr posix_code_domain variable.
static inline constexpr const _posix_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("posix domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
return static_cast<const posix_code &>(code).value() != 0; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const posix_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const posix_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
if(static_cast<int>(c2.value()) == c1.value())
{
return true;
}
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const posix_code &>(code); // NOLINT
return generic_code(static_cast<errc>(c.value()));
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const posix_code &>(code); // NOLINT
return _make_string_ref(c.value());
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const posix_code &>(code); // NOLINT
throw status_error<_posix_code_domain>(c);
}
#endif
};
//! A constexpr source variable for the POSIX code domain, which is that of `errno`. Returned by `_posix_code_domain::get()`.
constexpr _posix_code_domain posix_code_domain;
inline constexpr const _posix_code_domain &_posix_code_domain::get()
{
return posix_code_domain;
}
namespace mixins
{
template <class Base> inline posix_code mixin<Base, _posix_code_domain>::current() noexcept
{
return posix_code(errno);
}
} // namespace mixins
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,228 @@
/* Proposed SG14 status_code
(C) 2018 - 2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: May 2020
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_QUICK_STATUS_CODE_FROM_ENUM_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES
#define BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 1
#endif
#include "generic_code.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
template <class Enum> class _quick_status_code_from_enum_domain;
//! A status code wrapping `Enum` generated from `quick_status_code_from_enum`.
template <class Enum> using quick_status_code_from_enum_code = status_code<_quick_status_code_from_enum_domain<Enum>>;
//! Defaults for an implementation of `quick_status_code_from_enum<Enum>`
template <class Enum> struct quick_status_code_from_enum_defaults
{
//! The type of the resulting code
using code_type = quick_status_code_from_enum_code<Enum>;
//! Used within `quick_status_code_from_enum` to define a mapping of enumeration value with its status code
struct mapping
{
//! The enumeration type
using enumeration_type = Enum;
//! The value being mapped
const Enum value;
//! A string representation for this enumeration value
const char *message;
//! A list of `errc` equivalents for this enumeration value
const std::initializer_list<errc> code_mappings;
};
//! Used within `quick_status_code_from_enum` to define mixins for the status code wrapping `Enum`
template <class Base> struct mixin : Base
{
using Base::Base;
};
};
/*! The implementation of the domain for status codes wrapping `Enum` generated from `quick_status_code_from_enum`.
*/
template <class Enum> class _quick_status_code_from_enum_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
using _src = quick_status_code_from_enum<Enum>;
public:
//! The value type of the quick status code from enum
using value_type = Enum;
using _base::string_ref;
constexpr _quick_status_code_from_enum_domain()
: status_code_domain(_src::domain_uuid, _uuid_size<detail::cstrlen(_src::domain_uuid)>())
{
}
_quick_status_code_from_enum_domain(const _quick_status_code_from_enum_domain &) = default;
_quick_status_code_from_enum_domain(_quick_status_code_from_enum_domain &&) = default;
_quick_status_code_from_enum_domain &operator=(const _quick_status_code_from_enum_domain &) = default;
_quick_status_code_from_enum_domain &operator=(_quick_status_code_from_enum_domain &&) = default;
~_quick_status_code_from_enum_domain() = default;
#if __cplusplus < 201402L && !defined(_MSC_VER)
static inline const _quick_status_code_from_enum_domain &get()
{
static _quick_status_code_from_enum_domain v;
return v;
}
#else
static inline constexpr const _quick_status_code_from_enum_domain &get();
#endif
virtual string_ref name() const noexcept override { return string_ref(_src::domain_name); }
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
// Not sure if a hash table is worth it here, most enumerations won't be long enough to be worth it
// Also, until C++ 20's consteval, the hash table would get emitted into the binary, bloating it
static BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const typename _src::mapping *_find_mapping(value_type v) noexcept
{
for(const auto &i : _src::value_mappings())
{
if(i.value == v)
{
return &i;
}
}
return nullptr;
}
virtual bool _do_failure(const status_code<void> &code) const noexcept override
{
assert(code.domain() == *this); // NOLINT
// If `errc::success` is in the generic code mapping, it is not a failure
const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES
assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table
#endif
if(mapping != nullptr)
{
for(errc ec : mapping->code_mappings)
{
if(ec == errc::success)
{
return false;
}
}
}
return true;
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override
{
assert(code1.domain() == *this); // NOLINT
const auto &c1 = static_cast<const quick_status_code_from_enum_code<value_type> &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const quick_status_code_from_enum_code<value_type> &>(code2); // NOLINT
return c1.value() == c2.value();
}
if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
const auto *mapping = _find_mapping(c1.value());
#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES
assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table
#endif
if(mapping != nullptr)
{
for(errc ec : mapping->code_mappings)
{
if(ec == c2.value())
{
return true;
}
}
}
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override
{
assert(code.domain() == *this); // NOLINT
const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES
assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table
#endif
if(mapping != nullptr)
{
if(mapping->code_mappings.size() > 0)
{
return *mapping->code_mappings.begin();
}
}
return errc::unknown;
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override
{
assert(code.domain() == *this); // NOLINT
const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
#if BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES
assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table
#endif
if(mapping != nullptr)
{
return string_ref(mapping->message);
}
return string_ref("unknown");
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override
{
assert(code.domain() == *this); // NOLINT
const auto &c = static_cast<const quick_status_code_from_enum_code<value_type> &>(code); // NOLINT
throw status_error<_quick_status_code_from_enum_domain>(c);
}
#endif
};
#if __cplusplus >= 201402L || defined(_MSC_VER)
template <class Enum> constexpr _quick_status_code_from_enum_domain<Enum> quick_status_code_from_enum_domain = {};
template <class Enum> inline constexpr const _quick_status_code_from_enum_domain<Enum> &_quick_status_code_from_enum_domain<Enum>::get()
{
return quick_status_code_from_enum_domain<Enum>;
}
#endif
namespace mixins
{
template <class Base, class Enum>
struct mixin<Base, _quick_status_code_from_enum_domain<Enum>> : public quick_status_code_from_enum<Enum>::template mixin<Base>
{
using quick_status_code_from_enum<Enum>::template mixin<Base>::mixin;
};
} // namespace mixins
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,402 @@
/* A partial result based on std::variant and proposed std::error
(C) 2020-2023 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
File Created: Jan 2020
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_RESULT_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_RESULT_HPP
#include "error.hpp"
#if __cplusplus >= 201703L || _HAS_CXX17
#if __has_include(<variant>)
#include <exception>
#include <variant>
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
template <class T> inline constexpr std::in_place_type_t<T> in_place_type{};
template <class T> class result;
//! \brief A trait for detecting result types
template <class T> struct is_result : public std::false_type
{
};
template <class T> struct is_result<result<T>> : public std::true_type
{
};
/*! \brief Exception type representing the failure to retrieve an error.
*/
class bad_result_access : public std::exception
{
public:
bad_result_access() = default;
//! Return an explanatory string
virtual const char *what() const noexcept override { return "bad result access"; } // NOLINT
};
namespace detail
{
struct void_
{
};
template <class T> using devoid = std::conditional_t<std::is_void_v<T>, void_, T>;
} // namespace detail
/*! \class result
\brief A imperfect `result<T>` type with its error type hardcoded to `error`, only available on C++ 17 or later.
Note that the proper `result<T>` type does not have the possibility of
valueless by exception state. This implementation is therefore imperfect.
*/
template <class T> class result : protected std::variant<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error, detail::devoid<T>>
{
using _base = std::variant<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error, detail::devoid<T>>;
static_assert(!std::is_reference_v<T>, "Type cannot be a reference");
static_assert(!std::is_array_v<T>, "Type cannot be an array");
static_assert(!std::is_same_v<T, BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error>, "Type cannot be a std::error");
// not success nor failure types
struct _implicit_converting_constructor_tag
{
};
struct _explicit_converting_constructor_tag
{
};
struct _implicit_constructor_tag
{
};
struct _implicit_in_place_value_constructor_tag
{
};
struct _implicit_in_place_error_constructor_tag
{
};
public:
//! The value type
using value_type = T;
//! The error type
using error_type = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error;
//! The value type, if it is available, else a usefully named unusable internal type
using value_type_if_enabled = detail::devoid<T>;
//! Used to rebind result types
template <class U> using rebind = result<U>;
protected:
constexpr void _check() const
{
if(_base::index() == 0)
{
std::get_if<0>(this)->throw_exception();
}
}
constexpr
#ifdef _MSC_VER
__declspec(noreturn)
#elif defined(__GNUC__) || defined(__clang__)
__attribute__((noreturn))
#endif
void _ub()
{
assert(false); // NOLINT
#if defined(__GNUC__) || defined(__clang__)
__builtin_unreachable();
#elif defined(_MSC_VER)
__assume(0);
#endif
}
public:
constexpr _base &_internal() noexcept { return *this; }
constexpr const _base &_internal() const noexcept { return *this; }
//! Default constructor is disabled
result() = delete;
//! Copy constructor
result(const result &) = delete;
//! Move constructor
result(result &&) = default;
//! Copy assignment
result &operator=(const result &) = delete;
//! Move assignment
result &operator=(result &&) = default;
//! Destructor
~result() = default;
//! Implicit result converting move constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>)) constexpr result(result<U> &&o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
: _base(std::move(o))
{
}
//! Implicit result converting copy constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>)) constexpr result(const result<U> &o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
: _base(o)
{
}
//! Explicit result converting move constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>)) constexpr explicit result(result<U> &&o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
: _base(std::move(o))
{
}
//! Explicit result converting copy constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class U)
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>)) constexpr explicit result(const result<U> &o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
: _base(o)
{
}
//! Anything which `std::variant<error, T>` will construct from, we shall implicitly construct from
using _base::_base;
//! Special case `in_place_type_t<void>`
constexpr explicit result(std::in_place_type_t<void> /*unused*/) noexcept
: _base(in_place_type<detail::void_>)
{
}
//! Implicit in-place converting error constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, long = 5) //
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
&&std::is_constructible_v<error_type, Arg1, Arg2, Args...>))
constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v<error_type, Arg1, Arg2, Args...>)
: _base(std::in_place_index<0>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
{
}
//! Implicit in-place converting value constructor
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, int = 5) //
BOOST_OUTCOME_SYSTEM_ERROR2_TREQUIRES(BOOST_OUTCOME_SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
&&std::is_constructible_v<value_type, Arg1, Arg2, Args...>))
constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, Arg1, Arg2, Args...>)
: _base(std::in_place_index<1>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
{
}
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
BOOST_OUTCOME_SYSTEM_ERROR2_TEMPLATE(class U, class... Args, //
class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<U, 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<U>::type, result>::value // not copy/move of self
&& !std::is_same<typename std::decay<U>::type, value_type>::value // not copy/move of value type
&& is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
&& std::is_constructible<error_type, MakeStatusCodeResult>::value)) // ADLed status code is compatible
constexpr result(U &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<U>(), std::declval<Args>()...))) // NOLINT
: _base(std::in_place_index<0>, make_status_code(static_cast<U &&>(v), static_cast<Args &&>(args)...))
{
}
//! Swap with another result
constexpr void swap(result &o) noexcept(std::is_nothrow_swappable_v<_base>) { _base::swap(o); }
//! Clone the result
constexpr result clone() const { return has_value() ? result(value()) : result(error().clone()); }
//! True if result has a value
constexpr bool has_value() const noexcept { return _base::index() == 1; }
//! True if result has a value
explicit operator bool() const noexcept { return has_value(); }
//! True if result has an error
constexpr bool has_error() const noexcept { return _base::index() == 0; }
//! Accesses the value if one exists, else calls `.error().throw_exception()`.
constexpr value_type_if_enabled &value() &
{
_check();
return std::get<1>(*this);
}
//! Accesses the value if one exists, else calls `.error().throw_exception()`.
constexpr const value_type_if_enabled &value() const &
{
_check();
return std::get<1>(*this);
}
//! Accesses the value if one exists, else calls `.error().throw_exception()`.
constexpr value_type_if_enabled &&value() &&
{
_check();
return std::get<1>(std::move(*this));
}
//! Accesses the value if one exists, else calls `.error().throw_exception()`.
constexpr const value_type_if_enabled &&value() const &&
{
_check();
return std::get<1>(std::move(*this));
}
//! Accesses the error if one exists, else throws `bad_result_access`.
constexpr error_type &error() &
{
if(!has_error())
{
#ifndef BOOST_NO_EXCEPTIONS
throw bad_result_access();
#else
abort();
#endif
}
return *std::get_if<0>(this);
}
//! Accesses the error if one exists, else throws `bad_result_access`.
constexpr const error_type &error() const &
{
if(!has_error())
{
#ifndef BOOST_NO_EXCEPTIONS
throw bad_result_access();
#else
abort();
#endif
}
return *std::get_if<0>(this);
}
//! Accesses the error if one exists, else throws `bad_result_access`.
constexpr error_type &&error() &&
{
if(!has_error())
{
#ifndef BOOST_NO_EXCEPTIONS
throw bad_result_access();
#else
abort();
#endif
}
return std::move(*std::get_if<0>(this));
}
//! Accesses the error if one exists, else throws `bad_result_access`.
constexpr const error_type &&error() const &&
{
if(!has_error())
{
#ifndef BOOST_NO_EXCEPTIONS
throw bad_result_access();
#else
abort();
#endif
}
return std::move(*std::get_if<0>(this));
}
//! Accesses the value, being UB if none exists
constexpr value_type_if_enabled &assume_value() &noexcept
{
if(!has_value())
{
_ub();
}
return *std::get_if<1>(this);
}
//! Accesses the error, being UB if none exists
constexpr const value_type_if_enabled &assume_value() const &noexcept
{
if(!has_value())
{
_ub();
}
return *std::get_if<1>(this);
}
//! Accesses the error, being UB if none exists
constexpr value_type_if_enabled &&assume_value() &&noexcept
{
if(!has_value())
{
_ub();
}
return std::move(*std::get_if<1>(this));
}
//! Accesses the error, being UB if none exists
constexpr const value_type_if_enabled &&assume_value() const &&noexcept
{
if(!has_value())
{
_ub();
}
return std::move(*std::get_if<1>(this));
}
//! Accesses the error, being UB if none exists
constexpr error_type &assume_error() &noexcept
{
if(!has_error())
{
_ub();
}
return *std::get_if<0>(this);
}
//! Accesses the error, being UB if none exists
constexpr const error_type &assume_error() const &noexcept
{
if(!has_error())
{
_ub();
}
return *std::get_if<0>(this);
}
//! Accesses the error, being UB if none exists
constexpr error_type &&assume_error() &&noexcept
{
if(!has_error())
{
_ub();
}
return std::move(*std::get_if<0>(this));
}
//! Accesses the error, being UB if none exists
constexpr const error_type &&assume_error() const &&noexcept
{
if(!has_error())
{
_ub();
}
return std::move(*std::get_if<0>(this));
}
};
//! True if the two results compare equal.
template <class T, class U, typename = decltype(std::declval<T>() == std::declval<U>())> constexpr inline bool operator==(const result<T> &a, const result<U> &b) noexcept
{
const auto &x = a._internal();
return x == b;
}
//! True if the two results compare unequal.
template <class T, class U, typename = decltype(std::declval<T>() != std::declval<U>())> constexpr inline bool operator!=(const result<T> &a, const result<U> &b) noexcept
{
const auto &x = a._internal();
return x != b;
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
#endif
#endif
@@ -0,0 +1,718 @@
/* 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
#endif
@@ -0,0 +1,464 @@
/* Proposed SG14 status_code
(C) 2018-2023 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 Allocator> class indirecting_domain;
/* We are severely limited by needing to retain C++ 11 compatibility when doing
constexpr string parsing. MSVC lets you throw exceptions within a constexpr
evaluation context when exceptions are globally disabled, but won't let you
divide by zero, even if never evaluated, ever in constexpr. GCC and clang won't
let you throw exceptions, ever, if exceptions are globally disabled. So let's
use the trick of divide by zero in constexpr on GCC and clang if and only if
exceptions are globally disabled.
*/
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
#endif
#if defined(__cpp_exceptions) || (defined(_MSC_VER) && !defined(__clang__))
#define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) throw msg
#else
#define BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR(msg) ((void) msg, 1 / 0)
#endif
constexpr inline unsigned long long parse_hex_byte(char c)
{
return ('0' <= c && c <= '9') ? (c - '0') :
('a' <= c && c <= 'f') ? (10 + c - 'a') :
('A' <= c && c <= 'F') ? (10 + c - 'A') :
BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("Invalid character in UUID");
}
constexpr inline unsigned long long parse_uuid2(const char *s)
{
return ((parse_hex_byte(s[0]) << 0) | (parse_hex_byte(s[1]) << 4) | (parse_hex_byte(s[2]) << 8) | (parse_hex_byte(s[3]) << 12) |
(parse_hex_byte(s[4]) << 16) | (parse_hex_byte(s[5]) << 20) | (parse_hex_byte(s[6]) << 24) | (parse_hex_byte(s[7]) << 28) |
(parse_hex_byte(s[9]) << 32) | (parse_hex_byte(s[10]) << 36) | (parse_hex_byte(s[11]) << 40) | (parse_hex_byte(s[12]) << 44) |
(parse_hex_byte(s[14]) << 48) | (parse_hex_byte(s[15]) << 52) | (parse_hex_byte(s[16]) << 56) | (parse_hex_byte(s[17]) << 60)) //
^ //
((parse_hex_byte(s[19]) << 0) | (parse_hex_byte(s[20]) << 4) | (parse_hex_byte(s[21]) << 8) | (parse_hex_byte(s[22]) << 12) |
(parse_hex_byte(s[24]) << 16) | (parse_hex_byte(s[25]) << 20) | (parse_hex_byte(s[26]) << 24) | (parse_hex_byte(s[27]) << 28) |
(parse_hex_byte(s[28]) << 32) | (parse_hex_byte(s[29]) << 36) | (parse_hex_byte(s[30]) << 40) | (parse_hex_byte(s[31]) << 44) |
(parse_hex_byte(s[32]) << 48) | (parse_hex_byte(s[33]) << 52) | (parse_hex_byte(s[34]) << 56) | (parse_hex_byte(s[35]) << 60));
}
template <size_t N> constexpr inline unsigned long long parse_uuid_from_array(const char (&uuid)[N])
{
return (N == 37) ? parse_uuid2(uuid) : ((N == 39) ? parse_uuid2(uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length"));
}
template <size_t N> constexpr inline unsigned long long parse_uuid_from_pointer(const char *uuid)
{
return (N == 36) ? parse_uuid2(uuid) : ((N == 38) ? parse_uuid2(uuid + 1) : BOOST_OUTCOME_SYSTEM_ERROR2_FAIL_CONSTEXPR("UUID does not have correct length"));
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
static constexpr unsigned long long test_uuid_parse = parse_uuid_from_array("430f1201-94fc-06c7-430f-120194fc06c7");
// static constexpr unsigned long long test_uuid_parse2 = parse_uuid_from_array("x30f1201-94fc-06c7-430f-120194fc06c7");
} // 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, class Allocator> friend class detail::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.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 ~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 constexpr bool empty() const noexcept { return _begin == _end; }
//! Returns the size of the string
constexpr size_type size() const noexcept { return _end - _begin; }
//! Returns a null terminated C string
constexpr const_pointer c_str() const noexcept { return _begin; }
//! Returns a null terminated C string
constexpr const_pointer data() const noexcept { return _begin; }
//! Returns the beginning of the string
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator begin() noexcept { return _begin; }
//! Returns the beginning of the string
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator begin() const noexcept { return _begin; }
//! Returns the beginning of the string
constexpr const_iterator cbegin() const noexcept { return _begin; }
//! Returns the end of the string
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 iterator end() noexcept { return _end; }
//! Returns the end of the string
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const_iterator end() const noexcept { return _end; }
//! Returns the end of the string
constexpr 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 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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)
{
}
/*! UUID constructor, where input is constexpr parsed into a `unique_id_type`.
*/
template <size_t N>
constexpr explicit status_code_domain(const char (&uuid)[N]) noexcept
: _id(detail::parse_uuid_from_array<N>(uuid))
{
}
template <size_t N> struct _uuid_size
{
};
//! Alternative UUID constructor
template <size_t N>
constexpr explicit status_code_domain(const char *uuid, _uuid_size<N> /*unused*/) noexcept
: _id(detail::parse_uuid_from_pointer<N>(uuid))
{
}
//! 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.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual string_ref name() const noexcept = 0;
//! Information about the payload of the code for this domain
struct payload_info_t
{
size_t payload_size{0}; //!< The payload size in bytes
size_t total_size{0}; //!< The total status code size in bytes (includes domain pointer and mixins state)
size_t total_alignment{1}; //!< The total status code alignment in bytes
payload_info_t() = default;
constexpr payload_info_t(size_t _payload_size, size_t _total_size, size_t _total_alignment)
: payload_size(_payload_size)
, total_size(_total_size)
, total_alignment(_total_alignment)
{
}
};
//! Information about this domain's payload
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual payload_info_t payload_info() const noexcept = 0;
protected:
//! True if code means failure.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0;
//! Return a reference to a string textually representing a code.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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 BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 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()`. You should return false here if your payload is not
// trivially copyable or would not fit.
virtual bool _do_erased_copy(status_code<void> &dst, const status_code<void> &src, payload_info_t dstinfo) const
{
// Note that dst may not have its domain set
const auto srcinfo = payload_info();
if(dstinfo.total_size < srcinfo.total_size)
{
return false;
}
const auto tocopy = (dstinfo.total_size > srcinfo.total_size) ? srcinfo.total_size : dstinfo.total_size;
memcpy(&dst, &src, tocopy);
return true;
} // NOLINT
// For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing.
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_erased_destroy(status_code<void> &code, payload_info_t info) const noexcept // NOLINT
{
(void) code;
(void) info;
}
};
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,141 @@
/* Proposed SG14 status_code
(C) 2018 - 2022 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_ERROR_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_ERROR_HPP
#include "status_code.hpp"
#include <exception> // for std::exception
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! Exception type representing a thrown status_code
*/
template <class DomainType> class status_error;
/*! The erased type edition of status_error.
*/
template <> class status_error<void> : public std::exception
{
protected:
//! Constructs an instance. Not publicly available.
status_error() = default;
//! Copy constructor. Not publicly available
status_error(const status_error &) = default;
//! Move constructor. Not publicly available
status_error(status_error &&) = default;
//! Copy assignment. Not publicly available
status_error &operator=(const status_error &) = default;
//! Move assignment. Not publicly available
status_error &operator=(status_error &&) = default;
//! Destructor. Not publicly available.
~status_error() override = default;
virtual const status_code<void> &_do_code() const noexcept = 0;
public:
//! The type of the status domain
using domain_type = void;
//! The type of the status code
using status_code_type = status_code<void>;
public:
//! The erased status code which generated this exception instance.
const status_code<void> &code() const noexcept { return _do_code(); }
};
/*! Exception type representing a thrown status_code
*/
template <class DomainType> class status_error : public status_error<void>
{
status_code<DomainType> _code;
typename DomainType::string_ref _msgref;
virtual const status_code<void> &_do_code() const noexcept override final { return _code; }
public:
//! The type of the status domain
using domain_type = DomainType;
//! The type of the status code
using status_code_type = status_code<DomainType>;
//! Constructs an instance
explicit status_error(status_code<DomainType> code)
: _code(static_cast<status_code<DomainType> &&>(code))
, _msgref(_code.message())
{
}
//! Return an explanatory string
virtual const char *what() const noexcept override { return _msgref.c_str(); } // NOLINT
//! Returns a reference to the code
const status_code_type &code() const & { return _code; }
//! Returns a reference to the code
status_code_type &code() & { return _code; }
//! Returns a reference to the code
const status_code_type &&code() const && { return _code; }
//! Returns a reference to the code
status_code_type &&code() && { return _code; }
};
/*! Exception type representing a thrown erased status_code
*/
template <class ErasedType> class status_error<detail::erased<ErasedType>> : public status_error<void>
{
status_code<detail::erased<ErasedType>> _code;
typename status_code_domain::string_ref _msgref;
virtual const status_code<detail::erased<ErasedType>> &_do_code() const noexcept override final { return _code; }
public:
//! The type of the status domain
using domain_type = void;
//! The type of the status code
using status_code_type = status_code<detail::erased<ErasedType>>;
//! Constructs an instance
explicit status_error(status_code<detail::erased<ErasedType>> code)
: _code(static_cast<status_code<detail::erased<ErasedType>> &&>(code))
, _msgref(_code.message())
{
}
//! Return an explanatory string
virtual const char *what() const noexcept override { return _msgref.c_str(); } // NOLINT
//! Returns a reference to the code
const status_code_type &code() const & { return _code; }
//! Returns a reference to the code
status_code_type &code() & { return _code; }
//! Returns a reference to the code
const status_code_type &&code() const && { return _code; }
//! Returns a reference to the code
status_code_type &&code() && { return _code; }
};
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,321 @@
/* Proposed SG14 status_code
(C) 2018 - 2021 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Aug 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_STD_ERROR_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_STD_ERROR_CODE_HPP
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
#include "posix_code.hpp"
#endif
#if defined(_WIN32) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#include "win32_code.hpp"
#endif
#include <system_error>
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
class _std_error_code_domain;
//! A `status_code` representing exactly a `std::error_code`
using std_error_code = status_code<_std_error_code_domain>;
namespace mixins
{
template <class Base> struct mixin<Base, _std_error_code_domain> : public Base
{
using Base::Base;
//! Implicit constructor from a `std::error_code`
inline mixin(std::error_code ec);
//! Returns the error code category
inline const std::error_category &category() const noexcept;
};
} // namespace mixins
/*! The implementation of the domain for `std::error_code` error codes.
*/
class _std_error_code_domain final : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
using _error_code_type = std::error_code;
using _error_category_type = std::error_category;
std::string _name;
static _base::string_ref _make_string_ref(_error_code_type c) noexcept
{
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
try
#endif
{
std::string msg = c.message();
auto *p = static_cast<char *>(malloc(msg.size() + 1)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to allocate message");
}
memcpy(p, msg.c_str(), msg.size() + 1);
return _base::atomic_refcounted_string_ref(p, msg.size());
}
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
catch(...)
{
return _base::string_ref("failed to allocate message");
}
#endif
}
public:
//! The value type of the `std::error_code` code, which stores the `int` from the `std::error_code`
using value_type = int;
using _base::string_ref;
//! Returns the error category singleton pointer this status code domain represents
const _error_category_type &error_category() const noexcept
{
auto ptr = 0x223a160d20de97b4 ^ this->id();
return *reinterpret_cast<const _error_category_type *>(ptr);
}
//! Default constructor
explicit _std_error_code_domain(const _error_category_type &category) noexcept
: _base(0x223a160d20de97b4 ^ reinterpret_cast<_base::unique_id_type>(&category))
, _name("std_error_code_domain(")
{
_name.append(category.name());
_name.push_back(')');
}
_std_error_code_domain(const _std_error_code_domain &) = default;
_std_error_code_domain(_std_error_code_domain &&) = default;
_std_error_code_domain &operator=(const _std_error_code_domain &) = default;
_std_error_code_domain &operator=(_std_error_code_domain &&) = default;
~_std_error_code_domain() = default;
static inline const _std_error_code_domain *get(_error_code_type ec);
virtual string_ref name() const noexcept override { return string_ref(_name.c_str(), _name.size()); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override;
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override;
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override;
virtual string_ref _do_message(const status_code<void> &code) const noexcept override;
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override;
#endif
};
namespace detail
{
extern inline _std_error_code_domain *std_error_code_domain_from_category(const std::error_category &category)
{
static constexpr size_t max_items = 64;
static struct storage_t
{
std::atomic<unsigned> _lock;
union item_t
{
int _init;
_std_error_code_domain domain;
constexpr item_t()
: _init(0)
{
}
~item_t() {}
} items[max_items];
size_t count{0};
void lock()
{
while(_lock.exchange(1, std::memory_order_acquire) != 0)
;
}
void unlock() { _lock.store(0, std::memory_order_release); }
storage_t() {}
~storage_t()
{
lock();
for(size_t n = 0; n < count; n++)
{
items[n].domain.~_std_error_code_domain();
}
unlock();
}
_std_error_code_domain *add(const std::error_category &category)
{
_std_error_code_domain *ret = nullptr;
lock();
for(size_t n = 0; n < count; n++)
{
if(items[n].domain.error_category() == category)
{
ret = &items[n].domain;
break;
}
}
if(ret == nullptr && count < max_items)
{
ret = new(std::addressof(items[count++].domain)) _std_error_code_domain(category);
}
unlock();
return ret;
}
} storage;
return storage.add(category);
}
} // namespace detail
namespace mixins
{
template <class Base>
inline mixin<Base, _std_error_code_domain>::mixin(std::error_code ec)
: Base(typename Base::_value_type_constructor{}, _std_error_code_domain::get(ec), ec.value())
{
}
template <class Base> inline const std::error_category &mixin<Base, _std_error_code_domain>::category() const noexcept
{
const auto &domain = static_cast<const _std_error_code_domain &>(this->domain());
return domain.error_category();
};
} // namespace mixins
inline const _std_error_code_domain *_std_error_code_domain::get(std::error_code ec)
{
auto *p = detail::std_error_code_domain_from_category(ec.category());
assert(p != nullptr);
if(p == nullptr)
{
abort();
}
return p;
}
inline bool _std_error_code_domain::_do_failure(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
return static_cast<const std_error_code &>(code).value() != 0; // NOLINT
}
inline bool _std_error_code_domain::_do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const std_error_code &>(code1); // NOLINT
const auto &cat1 = c1.category();
// Are we comparing to another wrapped error_code?
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const std_error_code &>(code2); // NOLINT
const auto &cat2 = c2.category();
// If the error code categories are identical, do literal comparison
if(cat1 == cat2)
{
return c1.value() == c2.value();
}
// Otherwise fall back onto the _generic_code comparison, which uses default_error_condition()
return false;
}
// Am I an error code with generic category?
if(cat1 == std::generic_category())
{
// Convert to generic code, and compare that
generic_code _c1(static_cast<errc>(c1.value()));
return _c1 == code2;
}
// Am I an error code with system category?
if(cat1 == std::system_category())
{
// Convert to POSIX or Win32 code, and compare that
#ifdef _WIN32
win32_code _c1((win32::DWORD) c1.value());
return _c1 == code2;
#elif !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX)
posix_code _c1(c1.value());
return _c1 == code2;
#endif
}
return false;
}
inline generic_code _std_error_code_domain::_generic_code(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
const auto &c = static_cast<const std_error_code &>(code); // NOLINT
// Ask my embedded error code for its mapping to std::errc, which is a subset of our generic_code errc.
std::error_condition cond(c.category().default_error_condition(c.value()));
if(cond.category() == std::generic_category())
{
return generic_code(static_cast<errc>(cond.value()));
}
#if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32)
if(cond.category() == std::system_category())
{
return generic_code(static_cast<errc>(cond.value()));
}
#endif
return errc::unknown;
}
inline _std_error_code_domain::string_ref _std_error_code_domain::_do_message(const status_code<void> &code) const noexcept
{
assert(code.domain() == *this);
const auto &c = static_cast<const std_error_code &>(code); // NOLINT
return _make_string_ref(_error_code_type(c.value(), c.category()));
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN inline void _std_error_code_domain::_do_throw_exception(const status_code<void> &code) const
{
assert(code.domain() == *this);
const auto &c = static_cast<const std_error_code &>(code); // NOLINT
throw std::system_error(std::error_code(c.value(), c.category()));
}
#endif
static_assert(sizeof(std_error_code) <= sizeof(void *) * 2, "std_error_code does not fit into a system_code!");
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
// Enable implicit construction of `std_error_code` from `std::error_code`.
namespace std
{
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);
}
} // namespace std
#endif
@@ -0,0 +1,70 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_SYSTEM_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_SYSTEM_CODE_HPP
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
#include "posix_code.hpp"
#else
#include "quick_status_code_from_enum.hpp"
#endif
#if defined(_WIN32) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#include "nt_code.hpp"
#include "win32_code.hpp"
// NOT "com_code.hpp"
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! An erased-mutable status code suitably large for all the system codes
which can be returned on this system.
For Windows, these might be:
- `com_code` (`HRESULT`) [you need to include "com_code.hpp" explicitly for this]
- `nt_code` (`LONG`)
- `win32_code` (`DWORD`)
For POSIX, `posix_code` is possible.
You are guaranteed that `system_code` can be transported by the compiler
in exactly two CPU registers.
*/
using system_code = erased_status_code<intptr_t>;
#ifndef NDEBUG
static_assert(sizeof(system_code) == 2 * sizeof(void *), "system_code is not exactly two pointers in size!");
static_assert(traits::is_move_bitcopying<system_code>::value, "system_code is not move bitcopying!");
#endif
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,157 @@
/* Proposed SG14 status_code
(C) 2018-2023 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: June 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_SYSTEM_CODE_FROM_EXCEPTION_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_SYSTEM_CODE_FROM_EXCEPTION_HPP
#include "system_code.hpp"
#include "status_error.hpp"
#include <exception> // for exception_ptr
#include <stdexcept> // for the exception types
#include <system_error> // for std::system_error
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
/*! A utility function which returns the closest matching system_code to a supplied
exception ptr.
*/
inline system_code system_code_from_exception(std::exception_ptr &&ep = std::current_exception(),
system_code not_matched = generic_code(errc::resource_unavailable_try_again)) noexcept
{
if(!ep)
{
return generic_code(errc::success);
}
try
{
try
{
std::rethrow_exception(ep);
}
catch(const status_error<void> &e)
{
try
{
system_code erased(in_place, e.code());
if(!erased.empty())
{
return erased;
}
}
catch(...)
{
// Source status code's do_erased_copy() routine refused to copy the original
// Process instead as if the source were not a status_error
}
throw;
}
catch(...)
{
throw;
}
}
catch(const std::invalid_argument & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::invalid_argument);
}
catch(const std::domain_error & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::argument_out_of_domain);
}
catch(const std::length_error & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::argument_list_too_long);
}
catch(const std::out_of_range & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::result_out_of_range);
}
catch(const std::logic_error & /*unused*/) /* base class for this group */
{
ep = std::exception_ptr();
return generic_code(errc::invalid_argument);
}
catch(const std::system_error &e) /* also catches ios::failure */
{
ep = std::exception_ptr();
if(e.code().category() == std::generic_category())
{
return generic_code(static_cast<errc>(static_cast<int>(e.code().value())));
}
if(e.code().category() == std::system_category())
{
#ifdef _WIN32
return win32_code(e.code().value());
#else
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
return posix_code(e.code().value());
#else
return generic_code(static_cast<errc>(e.code().value()));
#endif
#endif
}
// Don't know this error code category, can't wrap it into std_error_code
// as its payload won't fit into system_code, so fall through.
}
catch(const std::overflow_error & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::value_too_large);
}
catch(const std::range_error & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::result_out_of_range);
}
catch(const std::runtime_error & /*unused*/) /* base class for this group */
{
ep = std::exception_ptr();
return generic_code(errc::resource_unavailable_try_again);
}
catch(const std::bad_alloc & /*unused*/)
{
ep = std::exception_ptr();
return generic_code(errc::not_enough_memory);
}
catch(...)
{
}
return not_matched;
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif
@@ -0,0 +1,36 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_HPP
#include "error.hpp"
#endif
@@ -0,0 +1,239 @@
/* Proposed SG14 status_code
(C) 2018-2023 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_WIN32_CODE_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_WIN32_CODE_HPP
#if !defined(_WIN32) && !defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
#error This file should only be included on Windows
#endif
#include "quick_status_code_from_enum.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
//! \exclude
namespace win32
{
// A Win32 DWORD
using DWORD = unsigned long;
// Used to retrieve the current Win32 error code
extern DWORD __stdcall GetLastError();
// Used to retrieve a locale-specific message string for some error code
extern DWORD __stdcall FormatMessageW(DWORD dwFlags, const void *lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t *lpBuffer, DWORD nSize,
void /*va_list*/ *Arguments);
// Converts UTF-16 message string to UTF-8
extern int __stdcall WideCharToMultiByte(unsigned int CodePage, DWORD dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr,
int cbMultiByte, const char *lpDefaultChar, int *lpUsedDefaultChar);
#pragma comment(lib, "kernel32.lib")
#if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64))
#pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError")
#pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YAKKPEBXKKPEA_WKPEAX@Z=FormatMessageW")
#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YAHIKPEB_WHPEADHPEBDPEAH@Z=WideCharToMultiByte")
#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__)
#pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YGKXZ=_GetLastError@0")
#pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YGKKPBXKKPA_WKPAX@Z=_FormatMessageW@28")
#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YGHIKPB_WHPADHPBDPAH@Z=_WideCharToMultiByte@32")
#elif defined(__arm__) || defined(_M_ARM)
#pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError")
#pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YAKKPBXKKPA_WKPAX@Z=FormatMessageW")
#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YAHIKPB_WHPADHPBDPAH@Z=WideCharToMultiByte")
#else
#error Unknown architecture
#endif
} // namespace win32
class _win32_code_domain;
class _com_code_domain;
//! (Windows only) A Win32 error code, those returned by `GetLastError()`.
using win32_code = status_code<_win32_code_domain>;
//! (Windows only) A specialisation of `status_error` for the Win32 error code domain.
using win32_error = status_error<_win32_code_domain>;
namespace mixins
{
template <class Base> struct mixin<Base, _win32_code_domain> : public Base
{
using Base::Base;
//! Returns a `win32_code` for the current value of `GetLastError()`.
static inline win32_code current() noexcept;
};
} // namespace mixins
/*! (Windows only) The implementation of the domain for Win32 error codes, those returned by `GetLastError()`.
*/
class _win32_code_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
friend class _com_code_domain;
using _base = status_code_domain;
static int _win32_code_to_errno(win32::DWORD c)
{
switch(c)
{
case 0:
return 0;
#include "detail/win32_code_to_generic_code.ipp"
}
return -1;
}
//! Construct from a Win32 error code
static _base::string_ref _make_string_ref(win32::DWORD c) noexcept
{
wchar_t buffer[32768];
win32::DWORD wlen =
win32::FormatMessageW(0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, nullptr, c, 0, buffer, 32768, nullptr);
size_t allocation = wlen + (wlen >> 1);
win32::DWORD bytes;
if(wlen == 0)
{
return _base::string_ref("failed to get message from system");
}
for(;;)
{
auto *p = static_cast<char *>(malloc(allocation)); // NOLINT
if(p == nullptr)
{
return _base::string_ref("failed to get message from system");
}
bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr);
if(bytes != 0)
{
char *end = strchr(p, 0);
while(end[-1] == 10 || end[-1] == 13)
{
--end;
}
*end = 0; // NOLINT
return _base::atomic_refcounted_string_ref(p, end - p);
}
free(p); // NOLINT
if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/)
{
allocation += allocation >> 2;
continue;
}
return _base::string_ref("failed to get message from system");
}
}
public:
//! The value type of the win32 code, which is a `win32::DWORD`
using value_type = win32::DWORD;
using _base::string_ref;
public:
//! Default constructor
constexpr explicit _win32_code_domain(typename _base::unique_id_type id = 0x8cd18ee72d680f1b) noexcept
: _base(id)
{
}
_win32_code_domain(const _win32_code_domain &) = default;
_win32_code_domain(_win32_code_domain &&) = default;
_win32_code_domain &operator=(const _win32_code_domain &) = default;
_win32_code_domain &operator=(_win32_code_domain &&) = default;
~_win32_code_domain() = default;
//! Constexpr singleton getter. Returns the constexpr win32_code_domain variable.
static inline constexpr const _win32_code_domain &get();
virtual string_ref name() const noexcept override { return string_ref("win32 domain"); } // NOLINT
virtual payload_info_t payload_info() const noexcept override
{
return {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type),
(alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : alignof(status_code_domain *)};
}
protected:
virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
return static_cast<const win32_code &>(code).value() != 0; // NOLINT
}
virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT
{
assert(code1.domain() == *this);
const auto &c1 = static_cast<const win32_code &>(code1); // NOLINT
if(code2.domain() == *this)
{
const auto &c2 = static_cast<const win32_code &>(code2); // NOLINT
return c1.value() == c2.value();
}
if(code2.domain() == generic_code_domain)
{
const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT
if(static_cast<int>(c2.value()) == _win32_code_to_errno(c1.value()))
{
return true;
}
}
return false;
}
virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const win32_code &>(code); // NOLINT
return generic_code(static_cast<errc>(_win32_code_to_errno(c.value())));
}
virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const win32_code &>(code); // NOLINT
return _make_string_ref(c.value());
}
#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT
{
assert(code.domain() == *this);
const auto &c = static_cast<const win32_code &>(code); // NOLINT
throw status_error<_win32_code_domain>(c);
}
#endif
};
//! (Windows only) A constexpr source variable for the win32 code domain, which is that of `GetLastError()` (Windows). Returned by `_win32_code_domain::get()`.
constexpr _win32_code_domain win32_code_domain;
inline constexpr const _win32_code_domain &_win32_code_domain::get()
{
return win32_code_domain;
}
namespace mixins
{
template <class Base> inline win32_code mixin<Base, _win32_code_domain>::current() noexcept
{
return win32_code(win32::GetLastError());
}
} // namespace mixins
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif