2
0
mirror of https://github.com/boostorg/outcome.git synced 2026-01-19 04:22:13 +00:00

Begin work on issue #110 by moving traits into their own header, various utility types into config.

success_failure.hpp is now system_error clean.
This commit is contained in:
Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com)
2018-03-06 18:59:07 +00:00
parent ba86c03db0
commit 7c89acd9f5
12 changed files with 929 additions and 767 deletions

1
.clang-format-ignore Normal file
View File

@@ -0,0 +1 @@
single-header/.*

View File

@@ -138,6 +138,162 @@ exported Outcome v2 namespace.
#endif
#endif
#include <type_traits>
#if __cplusplus >= 201700 || _HAS_CXX17
#include <utility> // for in_place_type_t
OUTCOME_V2_NAMESPACE_BEGIN
template <class T> using in_place_type_t = std::in_place_type_t<T>;
using std::in_place_type;
OUTCOME_V2_NAMESPACE_END
#else
OUTCOME_V2_NAMESPACE_BEGIN
//! Aliases `std::in_place_type_t<T>` if on C++ 17 or later, else defined locally.
template <class T> struct in_place_type_t
{
explicit in_place_type_t() = default;
};
//! Aliases `std::in_place_type<T>` if on C++ 17 or later, else defined locally.
template <class T> constexpr in_place_type_t<T> in_place_type{};
OUTCOME_V2_NAMESPACE_END
#endif
OUTCOME_V2_NAMESPACE_BEGIN
namespace detail
{
// Test if type is an in_place_type_t
template <class T> struct is_in_place_type_t
{
static constexpr bool value = false;
};
template <class U> struct is_in_place_type_t<in_place_type_t<U>>
{
static constexpr bool value = true;
};
// Replace void with constructible void_type
struct empty_type
{
};
struct void_type
{
// We always compare true to another instance of me
constexpr bool operator==(void_type /*unused*/) const noexcept { return true; }
constexpr bool operator!=(void_type /*unused*/) const noexcept { return false; }
};
template <class T> using devoid = std::conditional_t<std::is_void<T>::value, void_type, T>;
template <class Output, class Input> using rebind_type5 = Output;
template <class Output, class Input>
using rebind_type4 = std::conditional_t< //
std::is_volatile<Input>::value, //
std::add_volatile_t<rebind_type5<Output, std::remove_volatile_t<Input>>>, //
rebind_type5<Output, Input>>;
template <class Output, class Input>
using rebind_type3 = std::conditional_t< //
std::is_const<Input>::value, //
std::add_const_t<rebind_type4<Output, std::remove_const_t<Input>>>, //
rebind_type4<Output, Input>>;
template <class Output, class Input>
using rebind_type2 = std::conditional_t< //
std::is_lvalue_reference<Input>::value, //
std::add_lvalue_reference_t<rebind_type3<Output, std::remove_reference_t<Input>>>, //
rebind_type3<Output, Input>>;
template <class Output, class Input>
using rebind_type = std::conditional_t< //
std::is_rvalue_reference<Input>::value, //
std::add_rvalue_reference_t<rebind_type2<Output, std::remove_reference_t<Input>>>, //
rebind_type2<Output, Input>>;
// static_assert(std::is_same_v<rebind_type<int, volatile const double &&>, volatile const int &&>, "");
/* True if type is the same or constructible. Works around a bug where clang + libstdc++
pukes on std::is_constructible<filesystem::path, void> (this bug is fixed upstream).
*/
template <class T, class U> struct _is_explicitly_constructible
{
static constexpr bool value = std::is_constructible<T, U>::value;
};
template <class T> struct _is_explicitly_constructible<T, T>
{
static constexpr bool value = true;
};
template <class T> struct _is_explicitly_constructible<T, void>
{
static constexpr bool value = false;
};
template <> struct _is_explicitly_constructible<void, void>
{
static constexpr bool value = false;
};
template <class T, class U> static constexpr bool is_explicitly_constructible = _is_explicitly_constructible<T, U>::value;
template <class T, class U> struct _is_implicitly_constructible
{
static constexpr bool value = std::is_convertible<U, T>::value;
};
template <class T> struct _is_implicitly_constructible<T, T>
{
static constexpr bool value = true;
};
template <class T> struct _is_implicitly_constructible<T, void>
{
static constexpr bool value = false;
};
template <> struct _is_implicitly_constructible<void, void>
{
static constexpr bool value = false;
};
template <class T, class U> static constexpr bool is_implicitly_constructible = _is_implicitly_constructible<T, U>::value;
// True if type is nothrow swappable
#if !defined(STANDARDESE_IS_IN_THE_HOUSE) && (_HAS_CXX17 || __cplusplus >= 201700)
template <class T> using is_nothrow_swappable = std::is_nothrow_swappable<T>;
#else
namespace _is_nothrow_swappable
{
using namespace std;
template <class T> constexpr inline T &ldeclval();
template <class T, class = void> struct is_nothrow_swappable : std::integral_constant<bool, false>
{
};
template <class T> struct is_nothrow_swappable<T, decltype(swap(ldeclval<T>(), ldeclval<T>()))> : std::integral_constant<bool, noexcept(swap(ldeclval<T>(), ldeclval<T>()))>
{
};
} // namespace _is_nothrow_swappable
template <class T> using is_nothrow_swappable = _is_nothrow_swappable::is_nothrow_swappable<T>;
#endif
OUTCOME_TEMPLATE(class T, class U)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(std::declval<T>() == std::declval<U>()))
inline bool safe_compare_equal(const T &a, const U &b) noexcept(noexcept(std::declval<T>() == std::declval<U>()))
{
// std::cout << "Taken " << typeid(T).name() << " == " << typeid(U).name() << " = " << (a == b) << std::endl;
return a == b;
}
template <class T, class U> inline bool safe_compare_equal(T && /*unused*/, U && /*unused*/) noexcept
{
// std::cout << "Fallback " << typeid(T).name() << " == " << typeid(U).name() << " = false" << std::endl;
return false;
}
OUTCOME_TEMPLATE(class T, class U)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(std::declval<T>() != std::declval<U>()))
inline bool safe_compare_notequal(const T &a, const U &b) noexcept(noexcept(std::declval<T>() != std::declval<U>()))
{
// std::cout << "Taken " << typeid(T).name() << " != " << typeid(U).name() << " = " << (a != b) << std::endl;
return a != b;
}
template <class T, class U> inline bool safe_compare_notequal(T && /*unused*/, U && /*unused*/) noexcept
{
// std::cout << "Fallback " << typeid(T).name() << " != " << typeid(U).name() << " = true" << std::endl;
return true;
}
} // namespace detail
OUTCOME_V2_NAMESPACE_END
#ifndef OUTCOME_THROW_EXCEPTION
#ifdef __cpp_exceptions
#define OUTCOME_THROW_EXCEPTION(expr) throw expr

View File

@@ -26,6 +26,7 @@ http://www.boost.org/LICENSE_1_0.txt)
#define OUTCOME_RESULT_STORAGE_HPP
#include "../success_failure.hpp"
#include "../trait.hpp"
#include "value_storage.hpp"
#include <system_error>
@@ -34,87 +35,6 @@ OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
namespace detail
{
/* True if type is the same or constructible. Works around a bug where clang + libstdc++
pukes on std::is_constructible<filesystem::path, void> (this bug is fixed upstream).
*/
template <class T, class U> struct _is_explicitly_constructible
{
static constexpr bool value = std::is_constructible<T, U>::value;
};
template <class T> struct _is_explicitly_constructible<T, T>
{
static constexpr bool value = true;
};
template <class T> struct _is_explicitly_constructible<T, void>
{
static constexpr bool value = false;
};
template <> struct _is_explicitly_constructible<void, void>
{
static constexpr bool value = false;
};
template <class T, class U> static constexpr bool is_explicitly_constructible = _is_explicitly_constructible<T, U>::value;
template <class T, class U> struct _is_implicitly_constructible
{
static constexpr bool value = std::is_convertible<U, T>::value;
};
template <class T> struct _is_implicitly_constructible<T, T>
{
static constexpr bool value = true;
};
template <class T> struct _is_implicitly_constructible<T, void>
{
static constexpr bool value = false;
};
template <> struct _is_implicitly_constructible<void, void>
{
static constexpr bool value = false;
};
template <class T, class U> static constexpr bool is_implicitly_constructible = _is_implicitly_constructible<T, U>::value;
// True if type is nothrow swappable
#if !defined(STANDARDESE_IS_IN_THE_HOUSE) && (_HAS_CXX17 || __cplusplus >= 201700)
template <class T> using is_nothrow_swappable = std::is_nothrow_swappable<T>;
#else
namespace _is_nothrow_swappable
{
using namespace std;
template <class T> constexpr inline T &ldeclval();
template <class T, class = void> struct is_nothrow_swappable : std::integral_constant<bool, false>
{
};
template <class T> struct is_nothrow_swappable<T, decltype(swap(ldeclval<T>(), ldeclval<T>()))> : std::integral_constant<bool, noexcept(swap(ldeclval<T>(), ldeclval<T>()))>
{
};
} // namespace _is_nothrow_swappable
template <class T> using is_nothrow_swappable = _is_nothrow_swappable::is_nothrow_swappable<T>;
#endif
OUTCOME_TEMPLATE(class T, class U)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(std::declval<T>() == std::declval<U>()))
inline bool safe_compare_equal(const T &a, const U &b) noexcept(noexcept(std::declval<T>() == std::declval<U>()))
{
// std::cout << "Taken " << typeid(T).name() << " == " << typeid(U).name() << " = " << (a == b) << std::endl;
return a == b;
}
template <class T, class U> inline bool safe_compare_equal(T && /*unused*/, U && /*unused*/) noexcept
{
// std::cout << "Fallback " << typeid(T).name() << " == " << typeid(U).name() << " = false" << std::endl;
return false;
}
OUTCOME_TEMPLATE(class T, class U)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(std::declval<T>() != std::declval<U>()))
inline bool safe_compare_notequal(const T &a, const U &b) noexcept(noexcept(std::declval<T>() != std::declval<U>()))
{
// std::cout << "Taken " << typeid(T).name() << " != " << typeid(U).name() << " = " << (a != b) << std::endl;
return a != b;
}
template <class T, class U> inline bool safe_compare_notequal(T && /*unused*/, U && /*unused*/) noexcept
{
// std::cout << "Fallback " << typeid(T).name() << " != " << typeid(U).name() << " = true" << std::endl;
return true;
}
template <class State, class E> constexpr inline void _set_error_is_errno(State & /*unused*/, const E & /*unused*/) {}
template <class State> constexpr inline void _set_error_is_errno(State &state, const std::error_code &error)
{
@@ -159,25 +79,13 @@ namespace policy
} // namespace policy
namespace detail
{
//! Predicate for permitting type to be used in outcome
template <class R> //
static constexpr bool type_can_be_used_in_result = //
(!std::is_reference<R>::value //
&& !detail::is_in_place_type_t<std::decay_t<R>>::value //
&& !detail::is_success_type<R>::value //
&& !detail::is_failure_type<R>::value //
&& !std::is_array<R>::value //
&& (std::is_void<R>::value || (std::is_object<R>::value //
&& std::is_destructible<R>::value)) //
);
//! The base implementation type of `result<R, EC, NoValuePolicy>`.
template <class R, class EC, class NoValuePolicy> //
OUTCOME_REQUIRES(type_can_be_used_in_result<R> &&type_can_be_used_in_result<EC> && (std::is_void<EC>::value || std::is_default_constructible<EC>::value)) //
template <class R, class EC, class NoValuePolicy> //
OUTCOME_REQUIRES(trait::type_can_be_used_in_result<R> &&trait::type_can_be_used_in_result<EC> && (std::is_void<EC>::value || std::is_default_constructible<EC>::value)) //
class result_storage
{
static_assert(type_can_be_used_in_result<R>, "The type R cannot be used in a result");
static_assert(type_can_be_used_in_result<EC>, "The type S cannot be used in a result");
static_assert(trait::type_can_be_used_in_result<R>, "The type R cannot be used in a result");
static_assert(trait::type_can_be_used_in_result<EC>, "The type S cannot be used in a result");
static_assert(std::is_void<EC>::value || std::is_default_constructible<EC>::value, "The type S must be void or default constructible");
friend NoValuePolicy;

View File

@@ -30,34 +30,11 @@ http://www.boost.org/LICENSE_1_0.txt)
#include <cstdint> // for uint32_t etc
#include <initializer_list>
#include <iosfwd> // for serialisation
#include <type_traits>
#include <utility> // for in_place_type_t
OUTCOME_V2_NAMESPACE_BEGIN
#if __cplusplus >= 201700 || _HAS_CXX17
template <class T> using in_place_type_t = std::in_place_type_t<T>;
using std::in_place_type;
#else
//! Aliases `std::in_place_type_t<T>` if on C++ 17 or later, else defined locally.
template <class T> struct in_place_type_t
{
explicit in_place_type_t() = default;
};
//! Aliases `std::in_place_type<T>` if on C++ 17 or later, else defined locally.
template <class T> constexpr in_place_type_t<T> in_place_type{};
#endif
namespace detail
{
// Test if type is an in_place_type_t
template <class T> struct is_in_place_type_t : std::false_type
{
};
template <class U> struct is_in_place_type_t<in_place_type_t<U>> : std::true_type
{
};
using status_bitfield_type = uint32_t;
static constexpr status_bitfield_type status_have_value = (1U << 0U);
static constexpr status_bitfield_type status_have_error = (1U << 1U);

View File

@@ -39,7 +39,7 @@ http://www.boost.org/LICENSE_1_0.txt)
OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
template <class R, class S = std::error_code, class P = std::exception_ptr, class NoValuePolicy = policy::default_policy<R, S, P>> //
OUTCOME_REQUIRES(detail::type_can_be_used_in_result<P> && (std::is_void<P>::value || std::is_default_constructible<P>::value)) //
OUTCOME_REQUIRES(trait::type_can_be_used_in_result<P> && (std::is_void<P>::value || std::is_default_constructible<P>::value)) //
class outcome;
namespace detail
@@ -211,8 +211,8 @@ or if `P` is `void`, do `throw bad_outcome_access()`
or if `S` is `void`, do `throw bad_outcome_access()`
- If `S` is none of the above, then it is undefined behaviour [`policy::all_narrow`]
*/
template <class R, class S, class P, class NoValuePolicy> //
OUTCOME_REQUIRES(detail::type_can_be_used_in_result<P> && (std::is_void<P>::value || std::is_default_constructible<P>::value)) //
template <class R, class S, class P, class NoValuePolicy> //
OUTCOME_REQUIRES(trait::type_can_be_used_in_result<P> && (std::is_void<P>::value || std::is_default_constructible<P>::value)) //
class OUTCOME_NODISCARD outcome
#if defined(DOXYGEN_IS_IN_THE_HOUSE) || defined(STANDARDESE_IS_IN_THE_HOUSE)
: public detail::outcome_failure_observers<detail::select_outcome_impl2<R, S, P, NoValuePolicy>, R, S, P, NoValuePolicy>,
@@ -222,7 +222,7 @@ class OUTCOME_NODISCARD outcome
: public detail::select_outcome_impl<R, S, P, NoValuePolicy>
#endif
{
static_assert(detail::type_can_be_used_in_result<P>, "The exception_type cannot be used");
static_assert(trait::type_can_be_used_in_result<P>, "The exception_type cannot be used");
static_assert(std::is_void<P>::value || std::is_default_constructible<P>::value, "exception_type must be void or default constructible");
using base = detail::select_outcome_impl<R, S, P, NoValuePolicy>;
friend NoValuePolicy;

View File

@@ -58,9 +58,9 @@ namespace policy
>>>;
} // namespace policy
template <class R, class S = std::error_code, class NoValuePolicy = policy::default_policy<R, S, void>> //
#if !defined(__GNUC__) || __GNUC__ >= 8 // GCC's constraints implementation is buggy
OUTCOME_REQUIRES(detail::type_can_be_used_in_result<R> &&detail::type_can_be_used_in_result<S> && (std::is_void<S>::value || std::is_default_constructible<S>::value)) //
template <class R, class S = std::error_code, class NoValuePolicy = policy::default_policy<R, S, void>> //
#if !defined(__GNUC__) || __GNUC__ >= 8 // GCC's constraints implementation is buggy
OUTCOME_REQUIRES(trait::type_can_be_used_in_result<R> &&trait::type_can_be_used_in_result<S> && (std::is_void<S>::value || std::is_default_constructible<S>::value)) //
#endif
class result;
@@ -211,14 +211,14 @@ then `throw std::system_error(error()|make_error_code(error()))` [\verbatim {{<a
or if `S` is `void`, do `throw bad_result_access()`
- If `S` is none of the above, then it is undefined behaviour [`policy::all_narrow`]
*/
template <class R, class S, class NoValuePolicy> //
#if !defined(__GNUC__) || __GNUC__ >= 8 // GCC's constraints implementation is buggy
OUTCOME_REQUIRES(detail::type_can_be_used_in_result<R> &&detail::type_can_be_used_in_result<S> && (std::is_void<S>::value || std::is_default_constructible<S>::value)) //
template <class R, class S, class NoValuePolicy> //
#if !defined(__GNUC__) || __GNUC__ >= 8 // GCC's constraints implementation is buggy
OUTCOME_REQUIRES(trait::type_can_be_used_in_result<R> &&trait::type_can_be_used_in_result<S> && (std::is_void<S>::value || std::is_default_constructible<S>::value)) //
#endif
class OUTCOME_NODISCARD result : public detail::result_final<R, S, NoValuePolicy>
{
static_assert(detail::type_can_be_used_in_result<R>, "The type R cannot be used in a result");
static_assert(detail::type_can_be_used_in_result<S>, "The type S cannot be used in a result");
static_assert(trait::type_can_be_used_in_result<R>, "The type R cannot be used in a result");
static_assert(trait::type_can_be_used_in_result<S>, "The type S cannot be used in a result");
static_assert(std::is_void<S>::value || std::is_default_constructible<S>::value, "The type S must be void or default constructible");
using base = detail::result_final<R, S, NoValuePolicy>;

View File

@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
#define OUTCOME_PREVIOUS_COMMIT_REF c0b1fb11f6c772ca6cef7f7331c062f6cdbba0e8
#define OUTCOME_PREVIOUS_COMMIT_DATE "2018-02-06 18:59:30 +00:00"
#define OUTCOME_PREVIOUS_COMMIT_UNIQUE c0b1fb11
#define OUTCOME_PREVIOUS_COMMIT_REF ba86c03db079fd5c87b1f7d0e9c0d43ab55cfe6a
#define OUTCOME_PREVIOUS_COMMIT_DATE "2018-03-06 09:15:57 +00:00"
#define OUTCOME_PREVIOUS_COMMIT_UNIQUE ba86c03d

View File

@@ -27,144 +27,8 @@ http://www.boost.org/LICENSE_1_0.txt)
#include "config.hpp"
#include <exception>
#include <system_error>
#include <type_traits>
OUTCOME_V2_NAMESPACE_BEGIN
namespace detail
{
// Replace void with constructible void_type
struct empty_type
{
};
struct void_type
{
// We always compare true to another instance of me
constexpr bool operator==(void_type /*unused*/) const noexcept { return true; }
constexpr bool operator!=(void_type /*unused*/) const noexcept { return false; }
};
template <class T> using devoid = std::conditional_t<std::is_void<T>::value, void_type, T>;
template <class Output, class Input> using rebind_type5 = Output;
template <class Output, class Input>
using rebind_type4 = std::conditional_t< //
std::is_volatile<Input>::value, //
std::add_volatile_t<rebind_type5<Output, std::remove_volatile_t<Input>>>, //
rebind_type5<Output, Input>>;
template <class Output, class Input>
using rebind_type3 = std::conditional_t< //
std::is_const<Input>::value, //
std::add_const_t<rebind_type4<Output, std::remove_const_t<Input>>>, //
rebind_type4<Output, Input>>;
template <class Output, class Input>
using rebind_type2 = std::conditional_t< //
std::is_lvalue_reference<Input>::value, //
std::add_lvalue_reference_t<rebind_type3<Output, std::remove_reference_t<Input>>>, //
rebind_type3<Output, Input>>;
template <class Output, class Input>
using rebind_type = std::conditional_t< //
std::is_rvalue_reference<Input>::value, //
std::add_rvalue_reference_t<rebind_type2<Output, std::remove_reference_t<Input>>>, //
rebind_type2<Output, Input>>;
// static_assert(std::is_same_v<rebind_type<int, volatile const double &&>, volatile const int &&>, "");
} // namespace detail
//! Namespace for policies
namespace policy
{
namespace detail
{
struct error_code_passthrough
{
};
/* Pass through `make_error_code` function for anything implicitly convertible to `std::error_code`.
\requires `T` is implicitly convertible to `std::error_code`.
*/
OUTCOME_TEMPLATE(class T)
OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_convertible<T, std::error_code>::value))
constexpr inline decltype(auto) make_error_code(T &&v, error_code_passthrough /*unused*/ = {}) { return std::forward<T>(v); }
template <size_t N, class T> constexpr inline void get(const T & /*unused*/);
struct tuple_passthrough
{
};
/* Pass through `make_error_code` function for any pair or tuple returning the first item.
\requires That `make_error_code(std::get<0>(std::declval<T>()))` is a valid expression.
*/
OUTCOME_TEMPLATE(class T)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(make_error_code(get<0>(std::declval<T>()))))
constexpr inline decltype(auto) make_error_code(T &&v, tuple_passthrough /* unused */ = {}) { return make_error_code(get<0>(std::forward<T>(v))); }
/* Pass through `make_exception_ptr` function for `std::exception_ptr`.
*/
inline std::exception_ptr make_exception_ptr(std::exception_ptr v) { return v; }
template <class T> constexpr inline decltype(auto) error_code(T &&v) { return make_error_code(std::forward<T>(v)); }
template <class T> constexpr inline decltype(auto) exception_ptr(T &&v) { return make_exception_ptr(std::forward<T>(v)); }
} // namespace detail
//! Used by policies to extract a `std::error_code` from some input `T` via ADL discovery of some `make_error_code(T)` function.
template <class T> constexpr inline decltype(auto) error_code(T &&v) { return detail::error_code(std::forward<T>(v)); }
//! Used by policies to extract a `std::exception_ptr` from some input `T` via ADL discovery of some `make_exception_ptr(T)` function.
template <class T> constexpr inline decltype(auto) exception_ptr(T &&v) { return detail::exception_ptr(std::forward<T>(v)); }
//! Override to define what the policies which throw a system error with payload ought to do for some particular `result.error()`.
template <class Error> constexpr inline void throw_as_system_error_with_payload(const Error &error)
{
(void) error;
static_assert(std::is_convertible<Error, std::error_code>::value || std::is_error_code_enum<std::decay_t<Error>>::value || std::is_error_condition_enum<std::decay_t<Error>>::value,
"To use the error_code_throw_as_system_error policy with a custom Error type, you must define a throw_as_system_error_with_payload() free function to say how to handle the payload");
OUTCOME_THROW_EXCEPTION(std::system_error(error_code(error)));
}
} // namespace policy
//! Namespace for traits
namespace trait
{
namespace detail
{
template <class T> using devoid = OUTCOME_V2_NAMESPACE::detail::devoid<T>;
template <size_t N, class T> constexpr inline void get(const T & /*unused*/);
constexpr inline void make_error_code(...);
// Also enable for any pair or tuple whose first item satisfies make_error_code()
template <class T, //
class R = decltype(make_error_code(get<0>(std::declval<T>()))) //
>
constexpr inline R make_error_code(T &&);
template <class T, typename V = decltype(make_error_code(std::declval<devoid<T>>()))> struct has_error_code : std::integral_constant<bool, std::is_base_of<std::error_code, std::decay_t<V>>::value || std::is_convertible<T, std::error_code>::value>
{
};
constexpr inline void make_exception_ptr(...);
template <class T, typename V = decltype(make_exception_ptr(std::declval<devoid<T>>()))> struct has_exception_ptr : std::integral_constant<bool, std::is_base_of<std::exception_ptr, std::decay_t<V>>::value || std::is_convertible<T, std::exception_ptr>::value>
{
};
} // namespace detail
/*! Trait for whether a free function `make_error_code(T)` returning a `std::error_code` exists or not.
Also returns true if `std::error_code` is convertible from T.
*/
template <class T> struct has_error_code : detail::has_error_code<T>
{
};
/*! Trait for whether a free function `make_error_code(T)` returning a `std::error_code` exists or not.
Also returns true if `std::error_code` is convertible from T.
*/
template <class T> constexpr bool has_error_code_v = has_error_code<T>::value;
/*! Trait for whether a free function `make_exception_ptr(T)` returning a `std::exception_ptr` exists or not.
Also returns true if `std::exception_ptr` is convertible from T.
*/
template <class T> struct has_exception_ptr : detail::has_exception_ptr<T>
{
};
/*! Trait for whether a free function `make_exception_ptr(T)` returning a `std::exception_ptr` exists or not.
Also returns true if `std::exception_ptr` is convertible from T.
*/
template <class T> constexpr bool has_exception_ptr_v = has_exception_ptr<T>::value;
} // namespace trait
/*! Type sugar for implicitly constructing a `result<>` with a successful state.
*/
template <class T> struct success_type
@@ -197,7 +61,7 @@ public:
OUTCOME_TEMPLATE(class U)
OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same<success_type, std::decay_t<U>>::value))
constexpr explicit success_type(U &&v)
: _value(std::forward<U>(v))
: _value(static_cast<U &&>(v))
{
}
@@ -210,9 +74,9 @@ public:
/// \group success_type_value
constexpr const value_type &value() const & { return _value; }
/// \group success_type_value
constexpr value_type &&value() && { return std::move(_value); }
constexpr value_type &&value() && { return static_cast<value_type &&>(_value); }
/// \group success_type_value
constexpr const value_type &&value() const && { return std::move(_value); }
constexpr const value_type &&value() const && { return static_cast<value_type &&>(_value); }
};
/*! Type sugar for implicitly constructing a `result<>` with a successful state.
*/
@@ -233,12 +97,12 @@ inline constexpr success_type<void> success() noexcept
*/
template <class T> inline constexpr success_type<std::decay_t<T>> success(T &&v)
{
return success_type<std::decay_t<T>>{std::forward<T>(v)};
return success_type<std::decay_t<T>>{static_cast<T &&>(v)};
}
/*! Type sugar for implicitly constructing a `result<>` with a failure state of error code and exception.
*/
template <class EC = std::error_code, class E = void> struct failure_type
template <class EC, class E = void> struct failure_type
{
//! The type of the error code
using error_type = EC;
@@ -268,8 +132,8 @@ public:
//! Initialising constructor
template <class U, class V>
constexpr explicit failure_type(U &&u, V &&v)
: _error(std::forward<U>(u))
, _exception(std::forward<V>(v))
: _error(static_cast<U &&>(u))
, _exception(static_cast<V &&>(v))
{
}
@@ -282,9 +146,9 @@ public:
/// \group failure_type_error
constexpr const error_type &error() const & { return _error; }
/// \group failure_type_error
constexpr error_type &&error() && { return std::move(_error); }
constexpr error_type &&error() && { return static_cast<error_type &&>(_error); }
/// \group failure_type_error
constexpr const error_type &&error() const && { return std::move(_error); }
constexpr const error_type &&error() const && { return static_cast<error_type &&>(_error); }
/*! Access exception.
\returns Reference to the held `exception_type` according to overload.
@@ -294,9 +158,9 @@ public:
/// \group failure_type_exception
constexpr const exception_type &exception() const & { return _exception; }
/// \group failure_type_exception
constexpr exception_type &&exception() && { return std::move(_exception); }
constexpr exception_type &&exception() && { return static_cast<exception_type &&>(_exception); }
/// \group failure_type_exception
constexpr const exception_type &&exception() const && { return std::move(_exception); }
constexpr const exception_type &&exception() const && { return static_cast<exception_type &&>(_exception); }
};
/*! Type sugar for implicitly constructing a `result<>` with a failure state of error code.
*/
@@ -332,7 +196,7 @@ public:
OUTCOME_TEMPLATE(class U)
OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same<failure_type, std::decay_t<U>>::value))
constexpr explicit failure_type(U &&u)
: _error(std::forward<U>(u))
: _error(static_cast<U &&>(u))
{
}
@@ -345,9 +209,9 @@ public:
/// \group failure_type_error2
constexpr const error_type &error() const & { return _error; }
/// \group failure_type_error2
constexpr error_type &&error() && { return std::move(_error); }
constexpr error_type &&error() && { return static_cast<error_type &&>(_error); }
/// \group failure_type_error2
constexpr const error_type &&error() const && { return std::move(_error); }
constexpr const error_type &&error() const && { return static_cast<error_type &&>(_error); }
};
/*! Type sugar for implicitly constructing a `result<>` with a failure state of exception.
*/
@@ -383,7 +247,7 @@ public:
OUTCOME_TEMPLATE(class V)
OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same<failure_type, std::decay_t<V>>::value))
constexpr explicit failure_type(V &&v)
: _exception(std::forward<V>(v))
: _exception(static_cast<V &&>(v))
{
}
@@ -396,41 +260,51 @@ public:
/// \group failure_type_exception2
constexpr const exception_type &exception() const & { return _exception; }
/// \group failure_type_exception2
constexpr exception_type &&exception() && { return std::move(_exception); }
constexpr exception_type &&exception() && { return static_cast<exception_type &&>(_exception); }
/// \group failure_type_exception2
constexpr const exception_type &&exception() const && { return std::move(_exception); }
constexpr const exception_type &&exception() const && { return static_cast<exception_type &&>(_exception); }
};
/*! Returns type sugar for implicitly constructing a `result<T>` with a failure state.
\effects Copies or moves the failure state supplied into the returned type sugar.
*/
template <class EC> inline constexpr failure_type<std::decay_t<EC>> failure(EC &&v)
{
return failure_type<std::decay_t<EC>>{std::forward<EC>(v)};
return failure_type<std::decay_t<EC>>{static_cast<EC &&>(v)};
}
/*! Returns type sugar for implicitly constructing a `result<T>` with a failure state.
\effects Copies or moves the failure state supplied into the returned type sugar.
*/
template <class EC, class E> inline constexpr failure_type<std::decay_t<EC>, std::decay_t<E>> failure(EC &&v, E &&w)
{
return failure_type<std::decay_t<EC>, std::decay_t<E>>{std::forward<EC>(v), std::forward<E>(w)};
return failure_type<std::decay_t<EC>, std::decay_t<E>>{static_cast<EC &&>(v), static_cast<E &&>(w)};
}
namespace detail
{
template <class T> struct is_success_type : std::false_type
template <class T> struct is_success_type
{
static constexpr bool value = false;
};
template <class T> struct is_success_type<success_type<T>> : std::true_type
template <class T> struct is_success_type<success_type<T>>
{
static constexpr bool value = true;
};
template <class T> struct is_failure_type : std::false_type
template <class T> struct is_failure_type
{
static constexpr bool value = false;
};
template <class EC, class E> struct is_failure_type<failure_type<EC, E>> : std::true_type
template <class EC, class E> struct is_failure_type<failure_type<EC, E>>
{
static constexpr bool value = true;
};
} // namespace detail
//! True if the type is a success type
template <class T> static constexpr bool is_success_type = detail::is_success_type<std::decay_t<T>>::value;
//! True if the type is a failure type
template <class T> static constexpr bool is_failure_type = detail::is_failure_type<std::decay_t<T>>::value;
OUTCOME_V2_NAMESPACE_END
#endif

151
include/outcome/trait.hpp Normal file
View File

@@ -0,0 +1,151 @@
/* Traits for Outcome
(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (59 commits)
File Created: March 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 OUTCOME_TRAIT_HPP
#define OUTCOME_TRAIT_HPP
#include "config.hpp"
#include <exception>
#include <system_error>
OUTCOME_V2_NAMESPACE_BEGIN
//! Namespace for policies
namespace policy
{
namespace detail
{
struct error_code_passthrough
{
};
/* Pass through `make_error_code` function for anything implicitly convertible to `std::error_code`.
\requires `T` is implicitly convertible to `std::error_code`.
*/
OUTCOME_TEMPLATE(class T)
OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_convertible<T, std::error_code>::value))
constexpr inline decltype(auto) make_error_code(T &&v, error_code_passthrough /*unused*/ = {}) { return std::forward<T>(v); }
template <size_t N, class T> constexpr inline void get(const T & /*unused*/);
struct tuple_passthrough
{
};
/* Pass through `make_error_code` function for any pair or tuple returning the first item.
\requires That `make_error_code(std::get<0>(std::declval<T>()))` is a valid expression.
*/
OUTCOME_TEMPLATE(class T)
OUTCOME_TREQUIRES(OUTCOME_TEXPR(make_error_code(get<0>(std::declval<T>()))))
constexpr inline decltype(auto) make_error_code(T &&v, tuple_passthrough /* unused */ = {}) { return make_error_code(get<0>(std::forward<T>(v))); }
/* Pass through `make_exception_ptr` function for `std::exception_ptr`.
*/
inline std::exception_ptr make_exception_ptr(std::exception_ptr v) { return v; }
template <class T> constexpr inline decltype(auto) error_code(T &&v) { return make_error_code(std::forward<T>(v)); }
template <class T> constexpr inline decltype(auto) exception_ptr(T &&v) { return make_exception_ptr(std::forward<T>(v)); }
} // namespace detail
//! Used by policies to extract a `std::error_code` from some input `T` via ADL discovery of some `make_error_code(T)` function.
template <class T> constexpr inline decltype(auto) error_code(T &&v) { return detail::error_code(std::forward<T>(v)); }
//! Used by policies to extract a `std::exception_ptr` from some input `T` via ADL discovery of some `make_exception_ptr(T)` function.
template <class T> constexpr inline decltype(auto) exception_ptr(T &&v) { return detail::exception_ptr(std::forward<T>(v)); }
//! Override to define what the policies which throw a system error with payload ought to do for some particular `result.error()`.
template <class Error> constexpr inline void throw_as_system_error_with_payload(const Error &error)
{
(void) error;
static_assert(std::is_convertible<Error, std::error_code>::value || std::is_error_code_enum<std::decay_t<Error>>::value || std::is_error_condition_enum<std::decay_t<Error>>::value,
"To use the error_code_throw_as_system_error policy with a custom Error type, you must define a throw_as_system_error_with_payload() free function to say how to handle the payload");
OUTCOME_THROW_EXCEPTION(std::system_error(error_code(error)));
}
} // namespace policy
//! Namespace for traits
namespace trait
{
namespace detail
{
template <class T> using devoid = OUTCOME_V2_NAMESPACE::detail::devoid<T>;
template <size_t N, class T> constexpr inline void get(const T & /*unused*/);
constexpr inline void make_error_code(...);
// Also enable for any pair or tuple whose first item satisfies make_error_code()
template <class T, //
class R = decltype(make_error_code(get<0>(std::declval<T>()))) //
>
constexpr inline R make_error_code(T &&);
template <class T, typename V = decltype(make_error_code(std::declval<devoid<T>>()))> struct has_error_code : std::integral_constant<bool, std::is_base_of<std::error_code, std::decay_t<V>>::value || std::is_convertible<T, std::error_code>::value>
{
};
constexpr inline void make_exception_ptr(...);
template <class T, typename V = decltype(make_exception_ptr(std::declval<devoid<T>>()))> struct has_exception_ptr : std::integral_constant<bool, std::is_base_of<std::exception_ptr, std::decay_t<V>>::value || std::is_convertible<T, std::exception_ptr>::value>
{
};
} // namespace detail
/*! Trait for whether a free function `make_error_code(T)` returning a `std::error_code` exists or not.
Also returns true if `std::error_code` is convertible from T.
*/
template <class T> struct has_error_code : detail::has_error_code<T>
{
};
/*! Trait for whether a free function `make_error_code(T)` returning a `std::error_code` exists or not.
Also returns true if `std::error_code` is convertible from T.
*/
template <class T> constexpr bool has_error_code_v = has_error_code<T>::value;
/*! Trait for whether a free function `make_exception_ptr(T)` returning a `std::exception_ptr` exists or not.
Also returns true if `std::exception_ptr` is convertible from T.
*/
template <class T> struct has_exception_ptr : detail::has_exception_ptr<T>
{
};
/*! Trait for whether a free function `make_exception_ptr(T)` returning a `std::exception_ptr` exists or not.
Also returns true if `std::exception_ptr` is convertible from T.
*/
template <class T> constexpr bool has_exception_ptr_v = has_exception_ptr<T>::value;
/*! Requirements predicate for permitting type to be used in result/outcome.
- Is not a reference.
- Is not an `in_place_type_t<>`.
- Is not a `success_type<>`.
- Is not a `failure_type<>`.
- Is not an array.
- Is `void`, or else is an Object and is Destructible.
*/
template <class R> //
static constexpr bool type_can_be_used_in_result = //
(!std::is_reference<R>::value //
&& !OUTCOME_V2_NAMESPACE::detail::is_in_place_type_t<std::decay_t<R>>::value //
&& !is_success_type<R> //
&& !is_failure_type<R> //
&& !std::is_array<R>::value //
&& (std::is_void<R>::value || (std::is_object<R>::value //
&& std::is_destructible<R>::value)) //
);
} // namespace trait
OUTCOME_V2_NAMESPACE_END
#endif

File diff suppressed because it is too large Load Diff