feat():initial version

This commit is contained in:
2026-06-09 20:16:47 +08:00
commit 85fbb3188c
14809 changed files with 3044607 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
/* Tells C++ coroutines about Outcome's result
(C) 2019-2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
File Created: Oct 2019
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_EXPERIMENTAL_COROUTINE_SUPPORT_HPP
#define BOOST_OUTCOME_EXPERIMENTAL_COROUTINE_SUPPORT_HPP
#include "../config.hpp"
#define BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN \
BOOST_OUTCOME_V2_NAMESPACE_BEGIN namespace experimental \
{ \
namespace awaitables \
{
//
#define BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN \
BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace experimental \
{ \
namespace awaitables \
{
//
#define BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END \
} \
} \
BOOST_OUTCOME_V2_NAMESPACE_END
#ifndef BOOST_NO_EXCEPTIONS
#include "status-code/system_code_from_exception.hpp"
BOOST_OUTCOME_V2_NAMESPACE_BEGIN
namespace awaitables
{
namespace detail
{
inline bool error_is_set(BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code &sc) noexcept { return sc.failure(); }
inline BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code error_from_exception(std::exception_ptr &&ep = std::current_exception(), BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code not_matched = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::generic_code(BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc::resource_unavailable_try_again)) noexcept
{
return BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code_from_exception(static_cast<std::exception_ptr &&>(ep), static_cast<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code &&>(not_matched));
}
} // namespace detail
} // namespace awaitables
BOOST_OUTCOME_V2_NAMESPACE_END
#endif
#include "../detail/coroutine_support.ipp"
#undef BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN
#undef BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN
#undef BOOST_OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END
#endif

View File

@@ -0,0 +1,82 @@
/* C interface for result
(C) 2017-2020 Niall Douglas <http://www.nedproductions.biz/> (6 commits)
File Created: Aug 2017
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_EXPERIMENTAL_RESULT_H
#define BOOST_OUTCOME_EXPERIMENTAL_RESULT_H
#include <stdint.h> // for intptr_t
#define BOOST_OUTCOME_C_DECLARE_RESULT(ident, R, S) \
struct cxx_result_##ident \
{ \
R value; \
unsigned flags; \
S error; \
}
#define BOOST_OUTCOME_C_RESULT(ident) struct cxx_result_##ident
#define BOOST_OUTCOME_C_RESULT_HAS_VALUE(r) (((r).flags & 1U) == 1U)
#define BOOST_OUTCOME_C_RESULT_HAS_ERROR(r) (((r).flags & 2U) == 2U)
#define BOOST_OUTCOME_C_RESULT_ERROR_IS_ERRNO(r) (((r).flags & (1U << 4U)) == (1U << 4U))
/***************************** <system_error2> support ******************************/
#define BOOST_OUTCOME_C_DECLARE_STATUS_CODE(ident, value_type) \
struct cxx_status_code_##ident \
{ \
void *domain; \
value_type value; \
};
#define BOOST_OUTCOME_C_STATUS_CODE(ident) struct cxx_status_code_##ident
struct cxx_status_code_posix
{
void *domain;
int value;
};
#define BOOST_OUTCOME_C_DECLARE_RESULT_ERRNO(ident, R) BOOST_OUTCOME_C_DECLARE_RESULT(posix_##ident, R, struct cxx_status_code_posix)
#define BOOST_OUTCOME_C_RESULT_ERRNO(ident) BOOST_OUTCOME_C_RESULT(posix_##ident)
struct cxx_status_code_system
{
void *domain;
intptr_t value;
};
#define BOOST_OUTCOME_C_DECLARE_RESULT_SYSTEM(ident, R) BOOST_OUTCOME_C_DECLARE_RESULT(system_##ident, R, struct cxx_status_code_system)
#define BOOST_OUTCOME_C_RESULT_SYSTEM(ident) BOOST_OUTCOME_C_RESULT(system_##ident)
#endif

View File

@@ -0,0 +1,240 @@
/* Proposed SG14 status_code
(C) 2018-2020 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;
template <class StatusCode> friend class detail::indirecting_domain;
using _base = status_code_domain;
//! Construct from a `HRESULT` error code
static _base::string_ref _make_string_ref(HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept
{
_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(), wlen + 1, p, 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
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

View File

@@ -0,0 +1,327 @@
/* 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_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_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_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
#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
/* A partially compliant implementation of C++20's std::bit_cast function contributed
by Jesse Towner. TODO FIXME Replace with C++ 20 bit_cast when available.
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;
};
template <class To, class From,
typename std::enable_if< //
is_bit_castable<To, From>::value //
&& is_static_castable<To, From>::value //
&& !is_union_castable<To, From>::value, //
bool>::type = true> //
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
template <class To, class From,
typename std::enable_if< //
is_bit_castable<To, From>::value //
&& !is_static_castable<To, From>::value //
&& is_union_castable<To, From>::value, //
bool>::type = true> //
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
template <class To, class From,
typename std::enable_if< //
is_bit_castable<To, From>::value //
&& !is_static_castable<To, From>::value //
&& !is_union_castable<To, From>::value, //
bool>::type = true> //
To bit_cast(const From &from) noexcept
{
bit_cast_union<To, From> ret;
memmove(&ret.source, &from, sizeof(ret.source));
return ret.target;
}
/* 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{}
{
}
};
template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && (sizeof(To) == sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(from); }
template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && is_static_castable<To, From>::value && (sizeof(To) < sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return static_cast<To>(bit_cast<erasure_integer_type<From, To>>(from)); }
template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && is_static_castable<To, From>::value && (sizeof(To) > sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(static_cast<erasure_integer_type<To, From>>(from)); }
template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) < sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept
{
return bit_cast<padded_erasure_object<To, sizeof(From) - sizeof(To)>>(from).value;
}
template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) > sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept
{
return bit_cast<To>(padded_erasure_object<From, sizeof(To) - sizeof(From)>{from});
}
} // 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(_WIN64)
#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YA_JHPEBX_K@Z=write")
#else
#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YAHHPBXI@Z=_write")
#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

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,65 @@
/* Proposed SG14 status_code
(C) 2018-2020 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 erased `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 = errored_status_code<erased<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

View File

@@ -0,0 +1,417 @@
/* Proposed SG14 status_code
(C) 2018-2020 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"
#include "status_code_ptr.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 erased 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`.
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
typename std::enable_if<!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
bool>::type = true>
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.
template <class Enum, //
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type, // Enumeration has been activated
typename std::enable_if<std::is_constructible<errored_status_code, QuickStatusCodeType>::value, // Its status code is compatible
bool>::type = true>
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 destructible and `sizeof(status_code) <= sizeof(status_code<erased<>>)`.
Does not check if domains are equal.
*/
template <class ErasedType, //
typename std::enable_if<detail::type_erasure_is_safe<ErasedType, value_type>::value, bool>::type = true>
explicit errored_status_code(const status_code<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<erased<ErasedType>> : public status_code<erased<ErasedType>>
{
using _base = status_code<erased<ErasedType>>;
using _base::success;
void _check()
{
if(_base::success())
{
std::terminate();
}
}
public:
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 and it would fit into our storage
template <class DomainType, //
typename std::enable_if<std::is_trivially_copyable<typename DomainType::value_type>::value //
&& detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
bool>::type = true>
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
template <class DomainType, //
typename std::enable_if<std::is_trivially_copyable<typename DomainType::value_type>::value //
&& detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
bool>::type = true>
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
template <class DomainType, //
typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
bool>::type = true>
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
template <class DomainType, //
typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
bool>::type = true>
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`.
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
typename std::enable_if<!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
bool>::type = true>
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.
template <class Enum, //
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type, // Enumeration has been activated
typename std::enable_if<std::is_constructible<errored_status_code, QuickStatusCodeType>::value, // Its status code is compatible
bool>::type = true>
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();
}
//! 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; }
};
namespace traits
{
template <class ErasedType> struct is_move_bitcopying<errored_status_code<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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // 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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // 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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // 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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // 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

View File

@@ -0,0 +1,479 @@
/* 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;
template <class StatusCode> friend class detail::indirecting_domain;
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
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 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> 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> 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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // ADL makes a status code
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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // ADL makes a status code
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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // ADL makes a status code
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)`.
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
typename std::enable_if<is_status_code<MakeStatusCodeResult>::value, bool>::type = true> // ADL makes a status code
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
>
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
>
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
>
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
>
inline bool operator!=(const T &a, const status_code<DomainType1> &b)
{
return !b.equivalent(QuickStatusCodeType(a));
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,158 @@
/* Proposed SG14 status_code
(C) 2020 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;
template <class StatusCode> friend class detail::indirecting_domain;
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
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

View File

@@ -0,0 +1,85 @@
/* Proposed SG14 status_code
(C) 2018-2020 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`.
*/
template <class DomainType, //
typename std::enable_if<std::is_same<std::ostream, typename std::decay<decltype(std::declval<std::ostream>() << std::declval<typename status_code<DomainType>::value_type>())>::type>::value, bool>::type = true>
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<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

View File

@@ -0,0 +1,221 @@
/* Proposed SG14 status_code
(C) 2018-2020 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(_WIN64)
#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPEAXPEB_W@Z=GetModuleHandleW")
#else
#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YGPAXPB_W@Z=__imp__GetModuleHandleW@4")
#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;
template <class StatusCode> friend class detail::indirecting_domain;
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
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

View File

@@ -0,0 +1,174 @@
/* Proposed SG14 status_code
(C) 2018-2020 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
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;
template <class StatusCode> friend class detail::indirecting_domain;
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(__gnu_linux__) && !defined(__ANDROID__) // handle glibc's weird strerror_r()
char *s = strerror_r(c, buffer, sizeof(buffer)); // NOLINT
if(s != nullptr)
{
strncpy(buffer, s, sizeof(buffer)); // NOLINT
buffer[1023] = 0;
}
#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
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

View File

@@ -0,0 +1,210 @@
/* 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
#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;
template <class StatusCode> friend class detail::indirecting_domain;
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); }
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());
assert(mapping != nullptr);
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());
assert(mapping != nullptr);
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());
assert(mapping != nullptr);
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());
assert(mapping != nullptr);
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

View File

@@ -0,0 +1,407 @@
/* A partial result based on std::variant and proposed std::error
(C) 2020 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
template <class U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
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
template <class U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
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
template <class U, std::enable_if_t<std::is_constructible_v<T, U>, bool> = true>
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
template <class U, std::enable_if_t<std::is_constructible_v<T, U>, bool> = true>
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
template <class Arg1, class Arg2, class... Args, //
std::enable_if_t<!(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...>,
bool> = true,
long = 5>
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
template <class Arg1, class Arg2, class... Args, //
std::enable_if_t<!(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...>,
bool> = true,
int = 5>
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`.
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
typename std::enable_if<!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
bool>::type = true>
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

View File

@@ -0,0 +1,568 @@
/* Proposed SG14 status_code
(C) 2018 - 2019 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
/*! A tag for an erased value type for `status_code<D>`.
Available only if `ErasedType` satisfies `traits::is_move_bitcopying<ErasedType>::value`.
*/
template <class ErasedType, //
typename std::enable_if<traits::is_move_bitcopying<ErasedType>::value, bool>::type = true>
struct erased
{
using value_type = 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<erased<T>>>
{
static constexpr bool value = true;
};
// 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...>;
} // 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)
{
}
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.
string_ref message() const noexcept { return (_domain != nullptr) ? _domain->_do_message(*this) : string_ref("(empty)"); }
//! True if code means success.
bool success() const noexcept { return (_domain != nullptr) ? !_domain->_do_failure(*this) : false; }
//! True if code means failure.
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> 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> 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>
{
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(&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)...)
{
}
};
} // namespace detail
/*! 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`.
*/
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`.
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
typename std::enable_if<!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
bool>::type = true>
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.
template <class Enum, //
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type, // Enumeration has been activated
typename std::enable_if<std::is_constructible<status_code, QuickStatusCodeType>::value, // Its status code is compatible
bool>::type = true>
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.
*/
template <class ErasedType, //
typename std::enable_if<detail::type_erasure_is_safe<ErasedType, value_type>::value, bool>::type = true>
constexpr explicit status_code(const status_code<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.
string_ref message() const noexcept { return this->_domain ? string_ref(this->domain()._do_message(*this)) : 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<erased<ErasedType>> : public mixins::mixin<detail::status_code_storage<erased<ErasedType>>, erased<ErasedType>>
{
template <class T> friend class status_code;
using _base = mixins::mixin<detail::status_code_storage<erased<ErasedType>>, 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
~status_code()
{
if(nullptr != this->_domain)
{
this->_domain->_do_erased_destroy(*this, sizeof(*this));
}
}
//! Return a copy of the erased code by asking the domain to perform the erased copy.
status_code clone() const
{
if(nullptr == this->_domain)
{
return {};
}
status_code x;
this->_domain->_do_erased_copy(x, *this, sizeof(*this));
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 and it would fit into our storage
template <class DomainType, //
typename std::enable_if<std::is_trivially_copyable<typename DomainType::value_type>::value //
&& detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value,
bool>::type = true>
constexpr status_code(const status_code<DomainType> &v) noexcept // NOLINT
: _base(typename _base::_value_type_constructor{}, &v.domain(), 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
template <class DomainType, //
typename std::enable_if<detail::type_erasure_is_safe<value_type, typename DomainType::value_type>::value, bool>::type = true>
BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 status_code(status_code<DomainType> &&v) noexcept // NOLINT
: _base(typename _base::_value_type_constructor{}, &v.domain(), detail::erasure_cast<value_type>(v.value()))
{
v._domain = nullptr;
}
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
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
typename std::enable_if<!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
bool>::type = true>
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.
template <class Enum, //
class QuickStatusCodeType = typename quick_status_code_from_enum<Enum>::code_type, // Enumeration has been activated
typename std::enable_if<std::is_constructible<status_code, QuickStatusCodeType>::value, // Its status code is compatible
bool>::type = true>
constexpr status_code(Enum &&v) noexcept(std::is_nothrow_constructible<status_code, QuickStatusCodeType>::value) // NOLINT
: status_code(QuickStatusCodeType(static_cast<Enum &&>(v)))
{
}
/**** 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; }
};
namespace traits
{
template <class ErasedType> struct is_move_bitcopying<status_code<erased<ErasedType>>>
{
static constexpr bool value = true;
};
} // namespace traits
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,427 @@
/* Proposed SG14 status_code
(C) 2018-2020 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 indirecting_domain;
template <class T> struct status_code_sizer
{
void *a;
T b;
};
template <class To, class From> struct type_erasure_is_safe
{
static constexpr bool value = traits::is_move_bitcopying<From>::value //
&& (sizeof(status_code_sizer<From>) <= sizeof(status_code_sizer<To>));
};
/* 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> friend class 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.
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.
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
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
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
~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 bool empty() const noexcept { return _begin == _end; }
//! Returns the size of the string
size_type size() const noexcept { return _end - _begin; }
//! Returns a null terminated C string
const_pointer c_str() const noexcept { return _begin; }
//! Returns a null terminated C string
const_pointer data() const noexcept { return _begin; }
//! Returns the beginning of the string
iterator begin() noexcept { return _begin; }
//! Returns the beginning of the string
const_iterator begin() const noexcept { return _begin; }
//! Returns the beginning of the string
const_iterator cbegin() const noexcept { return _begin; }
//! Returns the end of the string
iterator end() noexcept { return _end; }
//! Returns the end of the string
const_iterator end() const noexcept { return _end; }
//! Returns the end of the string
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 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.
virtual string_ref name() const noexcept = 0;
protected:
//! True if code means failure.
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.
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.
virtual generic_code _generic_code(const status_code<void> &code) const noexcept = 0;
//! Return a reference to a string textually representing a code.
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 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()`.
virtual void _do_erased_copy(status_code<void> &dst, const status_code<void> &src, size_t bytes) const { memcpy(&dst, &src, bytes); } // NOLINT
// For a `status_code<erased<T>>` only, destroy the erased value type. Default implementation does nothing.
virtual void _do_erased_destroy(status_code<void> &code, size_t bytes) const noexcept // NOLINT
{
(void) code;
(void) bytes;
}
};
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,179 @@
/* Pointer to a SG14 status_code
(C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Sep 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_PTR_HPP
#define BOOST_OUTCOME_SYSTEM_ERROR2_STATUS_CODE_PTR_HPP
#include "status_code.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
namespace detail
{
template <class StatusCode> class indirecting_domain : public status_code_domain
{
template <class DomainType> friend class status_code;
using _base = status_code_domain;
public:
using value_type = StatusCode *;
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
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 typename StatusCode::domain_type()._do_failure(*c.value());
}
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 typename StatusCode::domain_type()._do_equivalent(*c1.value(), 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 typename StatusCode::domain_type()._generic_code(*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 _mycode &>(code); // NOLINT
return typename StatusCode::domain_type()._do_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);
const auto &c = static_cast<const _mycode &>(code); // NOLINT
typename StatusCode::domain_type()._do_throw_exception(*c.value());
}
#endif
virtual void _do_erased_copy(status_code<void> &dst, const status_code<void> &src, size_t /*unused*/) const override // NOLINT
{
// Note that dst will not have its domain set
assert(src.domain() == *this);
auto &d = static_cast<_mycode &>(dst); // NOLINT
const auto &_s = static_cast<const _mycode &>(src); // NOLINT
const StatusCode &s = *_s.value();
new(&d) _mycode(in_place, new StatusCode(s));
}
virtual void _do_erased_destroy(status_code<void> &code, size_t /*unused*/) const noexcept override // NOLINT
{
assert(code.domain() == *this);
auto &c = static_cast<_mycode &>(code); // NOLINT
delete c.value(); // NOLINT
}
};
#if __cplusplus >= 201402L || defined(_MSC_VER)
template <class StatusCode> constexpr indirecting_domain<StatusCode> _indirecting_domain{};
template <class StatusCode> inline constexpr const indirecting_domain<StatusCode> &indirecting_domain<StatusCode>::get() { return _indirecting_domain<StatusCode>; }
#endif
} // namespace detail
/*! Make an erased status code which indirects to a dynamically allocated status code.
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 due to `bad_alloc`.
*/
template <class T, typename std::enable_if<is_status_code<T>::value, bool>::type = true> //
inline status_code<erased<typename std::add_pointer<typename std::decay<T>::type>::type>> make_status_code_ptr(T &&v)
{
using status_code_type = typename std::decay<T>::type;
return status_code<detail::indirecting_domain<status_code_type>>(in_place, new status_code_type(static_cast<T &&>(v)));
}
/*! If a status code refers to a `status_code_ptr` which indirects to a status
code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null.
*/
template <class StatusCode, class U, typename std::enable_if<is_status_code<StatusCode>::value, bool>::type = true> inline StatusCode *get_if(status_code<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
template <class StatusCode, class U, typename std::enable_if<is_status_code<StatusCode>::value, bool>::type = true> inline const StatusCode *get_if(const status_code<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 `status_code_ptr`, 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<erased<U>> &v) noexcept
{
return 0xc44f7bdeb2cc50e9 ^ v.domain().id();
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,104 @@
/* Proposed SG14 status_code
(C) 2018-2020 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_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;
public:
//! The type of the status domain
using domain_type = void;
//! The type of the status code
using status_code_type = status_code<void>;
};
/*! 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;
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; }
};
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,302 @@
/* Proposed SG14 status_code
(C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
File Created: Aug 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_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;
template <class StatusCode> friend class detail::indirecting_domain;
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
{
try
{
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());
}
catch(...)
{
return _base::string_ref("failed to allocate message");
}
}
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
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(&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.
return generic_code(static_cast<errc>(c.category().default_error_condition(c.value()).value()));
}
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

View File

@@ -0,0 +1,70 @@
/* Proposed SG14 status_code
(C) 2018-2020 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 = status_code<erased<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

View File

@@ -0,0 +1,126 @@
/* Proposed SG14 status_code
(C) 2018-2020 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 <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
{
std::rethrow_exception(ep);
}
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
return posix_code(e.code().value());
#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

View File

@@ -0,0 +1,36 @@
/* Proposed SG14 status_code
(C) 2018-2020 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

View File

@@ -0,0 +1,221 @@
/* Proposed SG14 status_code
(C) 2018-2020 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(_WIN64)
#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")
#else
#pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YGKXZ=__imp__GetLastError@0")
#pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YGKKPBXKKPA_WKPAX@Z=__imp__FormatMessageW@28")
#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YGHIKPB_WHPADHPBDPAH@Z=__imp__WideCharToMultiByte@32")
#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;
template <class StatusCode> friend class detail::indirecting_domain;
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
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

View File

@@ -0,0 +1,118 @@
/* A less simple result type
(C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (17 commits)
File Created: Apr 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_EXPERIMENTAL_STATUS_OUTCOME_HPP
#define BOOST_OUTCOME_EXPERIMENTAL_STATUS_OUTCOME_HPP
#include "../basic_outcome.hpp"
#include "../detail/trait_std_exception.hpp"
#include "status_result.hpp"
#include "boost/exception_ptr.hpp"
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
template <class DomainType> inline std::exception_ptr basic_outcome_failure_exception_from_error(const status_code<DomainType> &sc)
{
(void) sc;
#ifndef BOOST_NO_EXCEPTIONS
try
{
sc.throw_exception();
}
catch(...)
{
return std::current_exception();
}
#endif
return {};
}
BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
namespace experimental
{
namespace policy
{
template <class T, class EC, class E>
using default_status_outcome_policy = std::conditional_t< //
std::is_void<EC>::value && std::is_void<E>::value, //
BOOST_OUTCOME_V2_NAMESPACE::policy::terminate, //
std::conditional_t<(is_status_code<EC>::value || is_errored_status_code<EC>::value) && (std::is_void<E>::value || BOOST_OUTCOME_V2_NAMESPACE::trait::is_exception_ptr_available<E>::value), //
status_code_throw<T, EC, E>, //
BOOST_OUTCOME_V2_NAMESPACE::policy::fail_to_compile_observers //
>>;
} // namespace policy
/*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
template <class R, class S = system_code, class P = std::exception_ptr, class NoValuePolicy = policy::default_status_outcome_policy<R, S, P>> //
using status_outcome = basic_outcome<R, S, P, NoValuePolicy>;
namespace policy
{
template <class T, class DomainType, class E> struct status_code_throw<T, status_code<DomainType>, E> : base
{
using _base = base;
template <class Impl> static constexpr void wide_value_check(Impl &&self)
{
if(!base::_has_value(static_cast<Impl &&>(self)))
{
if(base::_has_exception(static_cast<Impl &&>(self)))
{
BOOST_OUTCOME_V2_NAMESPACE::policy::detail::_rethrow_exception<trait::is_exception_ptr_available<E>::value>(base::_exception<T, status_code<DomainType>, E, status_code_throw>(static_cast<Impl &&>(self))); // NOLINT
}
if(base::_has_error(static_cast<Impl &&>(self)))
{
#ifndef BOOST_NO_EXCEPTIONS
base::_error(static_cast<Impl &&>(self)).throw_exception();
#else
BOOST_OUTCOME_THROW_EXCEPTION("wide value check failed");
#endif
}
}
}
template <class Impl> static constexpr void wide_error_check(Impl &&self) { _base::narrow_error_check(static_cast<Impl &&>(self)); }
template <class Impl> static constexpr void wide_exception_check(Impl &&self) { _base::narrow_exception_check(static_cast<Impl &&>(self)); }
};
template <class T, class DomainType, class E> struct status_code_throw<T, errored_status_code<DomainType>, E> : status_code_throw<T, status_code<DomainType>, E>
{
status_code_throw() = default;
using status_code_throw<T, status_code<DomainType>, E>::status_code_throw;
};
} // namespace policy
} // namespace experimental
BOOST_OUTCOME_V2_NAMESPACE_END
#endif

View File

@@ -0,0 +1,154 @@
/* A very simple result type
(C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
File Created: Apr 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_EXPERIMENTAL_STATUS_RESULT_HPP
#define BOOST_OUTCOME_EXPERIMENTAL_STATUS_RESULT_HPP
#include "../basic_result.hpp"
#include "../policy/fail_to_compile_observers.hpp"
#include "status-code/system_error2.hpp"
BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
namespace trait
{
namespace detail
{
// Shortcut this for lower build impact. Used to tell outcome's converting constructors
// that they can do E => EC or E => EP as necessary.
template <class DomainType> struct _is_error_code_available<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>>
{
static constexpr bool value = true;
using type = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>;
};
template <class DomainType> struct _is_error_code_available<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errored_status_code<DomainType>>
{
static constexpr bool value = true;
using type = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errored_status_code<DomainType>;
};
} // namespace detail
#if 0 // Do NOT enable weakened implicit construction for these types
template <class DomainType> struct is_error_type<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>>
{
static constexpr bool value = true;
};
template <> struct is_error_type<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc>
{
static constexpr bool value = true;
};
template <class DomainType, class Enum> struct is_error_type_enum<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>, Enum>
{
static constexpr bool value = boost::system::is_error_condition_enum<Enum>::value;
};
#endif
} // namespace trait
namespace detail
{
// Customise _set_error_is_errno
template <class State> constexpr inline void _set_error_is_errno(State &state, const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::generic_code & /*unused*/)
{
state._status.set_have_error_is_errno(true);
}
#ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
template <class State> constexpr inline void _set_error_is_errno(State &state, const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::posix_code & /*unused*/)
{
state._status.set_have_error_is_errno(true);
}
#endif
template <class State> constexpr inline void _set_error_is_errno(State &state, const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc & /*unused*/)
{
state._status.set_have_error_is_errno(true);
}
} // namespace detail
namespace experimental
{
using namespace BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE;
using BOOST_OUTCOME_V2_NAMESPACE::failure;
using BOOST_OUTCOME_V2_NAMESPACE::success;
namespace policy
{
using namespace BOOST_OUTCOME_V2_NAMESPACE::policy;
template <class T, class EC, class E> struct status_code_throw
{
static_assert(!std::is_same<T, T>::value,
"policy::status_code_throw not specialised for these types, did you use status_result<T, status_code<DomainType>, E>?");
};
template <class T, class DomainType> struct status_code_throw<T, status_code<DomainType>, void> : base
{
using _base = base;
template <class Impl> static constexpr void wide_value_check(Impl &&self)
{
if(!base::_has_value(static_cast<Impl &&>(self)))
{
if(base::_has_error(static_cast<Impl &&>(self)))
{
#ifndef BOOST_NO_EXCEPTIONS
base::_error(static_cast<Impl &&>(self)).throw_exception();
#else
BOOST_OUTCOME_THROW_EXCEPTION("wide value check failed");
#endif
}
}
}
template <class Impl> static constexpr void wide_error_check(Impl &&self) { _base::narrow_error_check(static_cast<Impl &&>(self)); }
};
template <class T, class DomainType>
struct status_code_throw<T, errored_status_code<DomainType>, void> : status_code_throw<T, status_code<DomainType>, void>
{
status_code_throw() = default;
using status_code_throw<T, status_code<DomainType>, void>::status_code_throw;
};
template <class T, class EC>
using default_status_result_policy = std::conditional_t< //
std::is_void<EC>::value, //
BOOST_OUTCOME_V2_NAMESPACE::policy::terminate, //
std::conditional_t<is_status_code<EC>::value || is_errored_status_code<EC>::value, //
status_code_throw<T, EC, void>, //
BOOST_OUTCOME_V2_NAMESPACE::policy::fail_to_compile_observers //
>>;
} // namespace policy
/*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
template <class R, class S = system_code, class NoValuePolicy = policy::default_status_result_policy<R, S>> //
using status_result = basic_result<R, S, NoValuePolicy>;
} // namespace experimental
BOOST_OUTCOME_V2_NAMESPACE_END
#endif