1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_IO_RESULT_HPP
10  
#ifndef BOOST_CAPY_IO_RESULT_HPP
11  
#define BOOST_CAPY_IO_RESULT_HPP
11  
#define BOOST_CAPY_IO_RESULT_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <system_error>
14  
#include <system_error>
15  

15  

16  
#include <cstddef>
16  
#include <cstddef>
17  
#include <tuple>
17  
#include <tuple>
18  
#include <type_traits>
18  
#include <type_traits>
19  
#include <utility>
19  
#include <utility>
20  

20  

21  
namespace boost {
21  
namespace boost {
22  
namespace capy {
22  
namespace capy {
23  

23  

24  
/** Result type for asynchronous I/O operations.
24  
/** Result type for asynchronous I/O operations.
25  

25  

26  
    This template provides a unified result type for async operations,
26  
    This template provides a unified result type for async operations,
27  
    always containing a `std::error_code` plus optional additional
27  
    always containing a `std::error_code` plus optional additional
28  
    values. It supports structured bindings via the tuple protocol.
28  
    values. It supports structured bindings via the tuple protocol.
29  

29  

30  
    @par Example
30  
    @par Example
31  
    @code
31  
    @code
32  
    auto [ec, n] = co_await s.read_some(buf);
32  
    auto [ec, n] = co_await s.read_some(buf);
33  
    if (ec) { ... }
33  
    if (ec) { ... }
34  
    @endcode
34  
    @endcode
35  

35  

36  
    @note Payload members are only meaningful when
36  
    @note Payload members are only meaningful when
37  
        `ec` does not indicate an error.
37  
        `ec` does not indicate an error.
38  

38  

39  
    @tparam Ts Ordered payload types following the leading
39  
    @tparam Ts Ordered payload types following the leading
40  
        `std::error_code`.
40  
        `std::error_code`.
41  
*/
41  
*/
42  
template<class... Ts>
42  
template<class... Ts>
43  
struct [[nodiscard]] io_result
43  
struct [[nodiscard]] io_result
44  
{
44  
{
45  
    /// The error code from the operation.
45  
    /// The error code from the operation.
46  
    std::error_code ec;
46  
    std::error_code ec;
47  

47  

48  
    /// The payload values. Unspecified when `ec` is set.
48  
    /// The payload values. Unspecified when `ec` is set.
49  
    std::tuple<Ts...> values;
49  
    std::tuple<Ts...> values;
50  

50  

51  
    /// Construct a default io_result.
51  
    /// Construct a default io_result.
52  
    io_result() = default;
52  
    io_result() = default;
53  

53  

54  
    /// Construct from an error code and payload values.
54  
    /// Construct from an error code and payload values.
55  
    io_result(std::error_code ec_, Ts... ts)
55  
    io_result(std::error_code ec_, Ts... ts)
56  
        : ec(ec_)
56  
        : ec(ec_)
57  
        , values(std::move(ts)...)
57  
        , values(std::move(ts)...)
58  
    {
58  
    {
59  
    }
59  
    }
60  

60  

61  
    /// @cond
61  
    /// @cond
62  
    template<std::size_t I>
62  
    template<std::size_t I>
63  
    decltype(auto) get() & noexcept
63  
    decltype(auto) get() & noexcept
64  
    {
64  
    {
65  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
65  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
66  
        if constexpr (I == 0) return (ec);
66  
        if constexpr (I == 0) return (ec);
67  
        else return std::get<I - 1>(values);
67  
        else return std::get<I - 1>(values);
68  
    }
68  
    }
69  

69  

70  
    template<std::size_t I>
70  
    template<std::size_t I>
71  
    decltype(auto) get() const& noexcept
71  
    decltype(auto) get() const& noexcept
72  
    {
72  
    {
73  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
73  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
74  
        if constexpr (I == 0) return (ec);
74  
        if constexpr (I == 0) return (ec);
75  
        else return std::get<I - 1>(values);
75  
        else return std::get<I - 1>(values);
76  
    }
76  
    }
77  

77  

78  
    template<std::size_t I>
78  
    template<std::size_t I>
79  
    decltype(auto) get() && noexcept
79  
    decltype(auto) get() && noexcept
80  
    {
80  
    {
81  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
81  
        static_assert(I < 1 + sizeof...(Ts), "index out of range");
82  
        if constexpr (I == 0) return std::move(ec);
82  
        if constexpr (I == 0) return std::move(ec);
83  
        else return std::get<I - 1>(std::move(values));
83  
        else return std::get<I - 1>(std::move(values));
84  
    }
84  
    }
85  
    /// @endcond
85  
    /// @endcond
86  
};
86  
};
87  

87  

88  
/// @cond
88  
/// @cond
89  
template<std::size_t I, class... Ts>
89  
template<std::size_t I, class... Ts>
90  
decltype(auto) get(io_result<Ts...>& r) noexcept
90  
decltype(auto) get(io_result<Ts...>& r) noexcept
91  
{
91  
{
92  
    return r.template get<I>();
92  
    return r.template get<I>();
93  
}
93  
}
94  

94  

95  
template<std::size_t I, class... Ts>
95  
template<std::size_t I, class... Ts>
96  
decltype(auto) get(io_result<Ts...> const& r) noexcept
96  
decltype(auto) get(io_result<Ts...> const& r) noexcept
97  
{
97  
{
98  
    return r.template get<I>();
98  
    return r.template get<I>();
99  
}
99  
}
100  

100  

101  
template<std::size_t I, class... Ts>
101  
template<std::size_t I, class... Ts>
102  
decltype(auto) get(io_result<Ts...>&& r) noexcept
102  
decltype(auto) get(io_result<Ts...>&& r) noexcept
103  
{
103  
{
104  
    return std::move(r).template get<I>();
104  
    return std::move(r).template get<I>();
105  
}
105  
}
106  
/// @endcond
106  
/// @endcond
107  

107  

108  
} // namespace capy
108  
} // namespace capy
109  
} // namespace boost
109  
} // namespace boost
110  

110  

111  
// Tuple protocol for structured bindings
111  
// Tuple protocol for structured bindings
112  
namespace std {
112  
namespace std {
113  

113  

114  
template<class... Ts>
114  
template<class... Ts>
115  
struct tuple_size<boost::capy::io_result<Ts...>>
115  
struct tuple_size<boost::capy::io_result<Ts...>>
116  
    : std::integral_constant<std::size_t, 1 + sizeof...(Ts)> {};
116  
    : std::integral_constant<std::size_t, 1 + sizeof...(Ts)> {};
117  

117  

118  
template<class... Ts>
118  
template<class... Ts>
119  
struct tuple_element<0, boost::capy::io_result<Ts...>>
119  
struct tuple_element<0, boost::capy::io_result<Ts...>>
120  
{
120  
{
121  
    using type = std::error_code;
121  
    using type = std::error_code;
122  
};
122  
};
123  

123  

124  
template<std::size_t I, class... Ts>
124  
template<std::size_t I, class... Ts>
125  
struct tuple_element<I, boost::capy::io_result<Ts...>>
125  
struct tuple_element<I, boost::capy::io_result<Ts...>>
126  
{
126  
{
127  
    using type = std::tuple_element_t<I - 1, std::tuple<Ts...>>;
127  
    using type = std::tuple_element_t<I - 1, std::tuple<Ts...>>;
128  
};
128  
};
129  

129  

130  
} // namespace std
130  
} // namespace std
131  

131  

132  
#endif // BOOST_CAPY_IO_RESULT_HPP
132  
#endif // BOOST_CAPY_IO_RESULT_HPP