1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_DECOMPOSES_TO_HPP
10  
#ifndef BOOST_CAPY_DECOMPOSES_TO_HPP
11  
#define BOOST_CAPY_DECOMPOSES_TO_HPP
11  
#define BOOST_CAPY_DECOMPOSES_TO_HPP
12  

12  

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

14  

15  
#include <system_error>
15  
#include <system_error>
16  
#include <concepts>
16  
#include <concepts>
17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <tuple>
18  
#include <tuple>
19  
#include <type_traits>
19  
#include <type_traits>
20  
#include <utility>
20  
#include <utility>
21  

21  

22  
namespace boost {
22  
namespace boost {
23  
namespace capy {
23  
namespace capy {
24  
namespace detail {
24  
namespace detail {
25  

25  

26  
struct any_type
26  
struct any_type
27  
{
27  
{
28  
    template <typename T>
28  
    template <typename T>
29  
    constexpr operator T() const noexcept;
29  
    constexpr operator T() const noexcept;
30  
};
30  
};
31  

31  

32  
template <typename T, std::size_t N>
32  
template <typename T, std::size_t N>
33  
concept is_tuple_n = requires {
33  
concept is_tuple_n = requires {
34  
    std::tuple_size<std::remove_cvref_t<T>>::value;
34  
    std::tuple_size<std::remove_cvref_t<T>>::value;
35  
} && std::tuple_size<std::remove_cvref_t<T>>::value == N;
35  
} && std::tuple_size<std::remove_cvref_t<T>>::value == N;
36  

36  

37  
// clang-format off
37  
// clang-format off
38  
template <typename T>
38  
template <typename T>
39  
concept is_decomposable_1 =
39  
concept is_decomposable_1 =
40  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
40  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
41  
        requires { std::remove_cvref_t<T>{ any_type{} }; } &&
41  
        requires { std::remove_cvref_t<T>{ any_type{} }; } &&
42  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; }
42  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; }
43  
    ) || is_tuple_n<T, 1>;
43  
    ) || is_tuple_n<T, 1>;
44  

44  

45  
template <typename T>
45  
template <typename T>
46  
concept is_decomposable_2 = 
46  
concept is_decomposable_2 = 
47  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
47  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
48  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; } &&
48  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; } &&
49  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; }
49  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; }
50  
    ) || is_tuple_n<T, 2>;
50  
    ) || is_tuple_n<T, 2>;
51  

51  

52  
template <typename T>
52  
template <typename T>
53  
concept is_decomposable_3 = 
53  
concept is_decomposable_3 = 
54  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
54  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
55  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; } &&
55  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; } &&
56  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; }
56  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; }
57  
    ) || is_tuple_n<T, 3>;
57  
    ) || is_tuple_n<T, 3>;
58  

58  

59  
template <typename T>
59  
template <typename T>
60  
concept is_decomposable_4 = 
60  
concept is_decomposable_4 = 
61  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
61  
    (std::is_aggregate_v<std::remove_cvref_t<T>> &&
62  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; } &&
62  
        requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; } &&
63  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{}, any_type{} }; }
63  
        !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{}, any_type{} }; }
64  
    ) || is_tuple_n<T, 4>;
64  
    ) || is_tuple_n<T, 4>;
65  

65  

66  
// clang-format on
66  
// clang-format on
67  

67  

68  
template <is_decomposable_1 T>
68  
template <is_decomposable_1 T>
69  
auto decomposed_types(T&& t)
69  
auto decomposed_types(T&& t)
70  
{
70  
{
71  
    auto [v0] = t;
71  
    auto [v0] = t;
72  
    return std::make_tuple(v0);
72  
    return std::make_tuple(v0);
73  
}
73  
}
74  

74  

75  
template <is_decomposable_2 T>
75  
template <is_decomposable_2 T>
76  
auto decomposed_types(T&& t)
76  
auto decomposed_types(T&& t)
77  
{
77  
{
78  
    auto [v0, v1] = t;
78  
    auto [v0, v1] = t;
79  
    return std::make_tuple(v0, v1);
79  
    return std::make_tuple(v0, v1);
80  
}
80  
}
81  

81  

82  
template <is_decomposable_3 T>
82  
template <is_decomposable_3 T>
83  
auto decomposed_types(T&& t)
83  
auto decomposed_types(T&& t)
84  
{
84  
{
85  
    auto [v0, v1, v2] = t;
85  
    auto [v0, v1, v2] = t;
86  
    return std::make_tuple(v0, v1, v2);
86  
    return std::make_tuple(v0, v1, v2);
87  
}
87  
}
88  

88  

89  
template <is_decomposable_4 T>
89  
template <is_decomposable_4 T>
90  
auto decomposed_types(T&& t)
90  
auto decomposed_types(T&& t)
91  
{
91  
{
92  
    auto [v0, v1, v2, v3] = t;
92  
    auto [v0, v1, v2, v3] = t;
93  
    return std::make_tuple(v0, v1, v2, v3);
93  
    return std::make_tuple(v0, v1, v2, v3);
94  
}
94  
}
95  

95  

96  
template <class T>
96  
template <class T>
97  
std::tuple<> decomposed_types(T&&)
97  
std::tuple<> decomposed_types(T&&)
98  
{
98  
{
99  
    return {};
99  
    return {};
100  
}
100  
}
101  

101  

102  
template<typename T>
102  
template<typename T>
103  
auto get_awaiter(T&& t)
103  
auto get_awaiter(T&& t)
104  
{
104  
{
105  
    if constexpr (requires { std::forward<T>(t).operator co_await(); })
105  
    if constexpr (requires { std::forward<T>(t).operator co_await(); })
106  
    {
106  
    {
107  
        return std::forward<T>(t).operator co_await();
107  
        return std::forward<T>(t).operator co_await();
108  
    }
108  
    }
109  
    else if constexpr (requires { operator co_await(std::forward<T>(t)); })
109  
    else if constexpr (requires { operator co_await(std::forward<T>(t)); })
110  
    {
110  
    {
111  
        return operator co_await(std::forward<T>(t));
111  
        return operator co_await(std::forward<T>(t));
112  
    }
112  
    }
113  
    else
113  
    else
114  
    {
114  
    {
115  
        return std::forward<T>(t);
115  
        return std::forward<T>(t);
116  
    }
116  
    }
117  
}
117  
}
118  

118  

119  
template<typename A>
119  
template<typename A>
120  
using awaitable_return_t = decltype(
120  
using awaitable_return_t = decltype(
121  
    get_awaiter(std::declval<A>()).await_resume()
121  
    get_awaiter(std::declval<A>()).await_resume()
122  
);
122  
);
123  

123  

124  
} // namespace detail
124  
} // namespace detail
125  

125  

126  
/** Concept for types that decompose to a specific typelist.
126  
/** Concept for types that decompose to a specific typelist.
127  

127  

128  
    A type satisfies `decomposes_to` if it can be decomposed via
128  
    A type satisfies `decomposes_to` if it can be decomposed via
129  
    structured bindings into the specified types. This includes
129  
    structured bindings into the specified types. This includes
130  
    aggregates with matching member types and tuple-like types
130  
    aggregates with matching member types and tuple-like types
131  
    with matching element types.
131  
    with matching element types.
132  

132  

133  
    @tparam T The type to decompose.
133  
    @tparam T The type to decompose.
134  
    @tparam Types The expected element types after decomposition.
134  
    @tparam Types The expected element types after decomposition.
135  

135  

136  
    @par Example
136  
    @par Example
137  
    @code
137  
    @code
138  
    struct result { int a; double b; };
138  
    struct result { int a; double b; };
139  

139  

140  
    static_assert(decomposes_to<result, int, double>);
140  
    static_assert(decomposes_to<result, int, double>);
141  
    static_assert(decomposes_to<std::tuple<int, double>, int, double>);
141  
    static_assert(decomposes_to<std::tuple<int, double>, int, double>);
142  
    @endcode
142  
    @endcode
143  
*/
143  
*/
144  
template <typename T, typename... Types>
144  
template <typename T, typename... Types>
145  
concept decomposes_to = requires(T&& t) {
145  
concept decomposes_to = requires(T&& t) {
146  
    { detail::decomposed_types(std::forward<T>(t)) } -> std::same_as<std::tuple<Types...>>;
146  
    { detail::decomposed_types(std::forward<T>(t)) } -> std::same_as<std::tuple<Types...>>;
147  
};
147  
};
148  

148  

149  
/** Concept for awaitables whose return type decomposes to a specific typelist.
149  
/** Concept for awaitables whose return type decomposes to a specific typelist.
150  

150  

151  
    A type satisfies `awaitable_decomposes_to` if it is an awaitable
151  
    A type satisfies `awaitable_decomposes_to` if it is an awaitable
152  
    (has `await_resume`) and its return type decomposes to the
152  
    (has `await_resume`) and its return type decomposes to the
153  
    specified typelist.
153  
    specified typelist.
154  

154  

155  
    @tparam A The awaitable type.
155  
    @tparam A The awaitable type.
156  
    @tparam Types The expected element types after decomposition.
156  
    @tparam Types The expected element types after decomposition.
157  

157  

158  
    @par Requirements
158  
    @par Requirements
159  
    @li `A` must be an awaitable (directly or via `operator co_await`)
159  
    @li `A` must be an awaitable (directly or via `operator co_await`)
160  
    @li The return type of `await_resume()` must decompose to `Types...`
160  
    @li The return type of `await_resume()` must decompose to `Types...`
161  

161  

162  
    @par Example
162  
    @par Example
163  
    @code
163  
    @code
164  
    // Constrain a function to accept only awaitables that return
164  
    // Constrain a function to accept only awaitables that return
165  
    // a decomposable result of (error_code, size_t)
165  
    // a decomposable result of (error_code, size_t)
166  
    template<typename A>
166  
    template<typename A>
167  
        requires awaitable_decomposes_to<A, std::error_code, std::size_t>
167  
        requires awaitable_decomposes_to<A, std::error_code, std::size_t>
168  
    task<void> process(A&& op)
168  
    task<void> process(A&& op)
169  
    {
169  
    {
170  
        auto [ec, n] = co_await std::forward<A>(op);
170  
        auto [ec, n] = co_await std::forward<A>(op);
171  
        if (ec)
171  
        if (ec)
172  
            co_return;
172  
            co_return;
173  
        // process n bytes...
173  
        // process n bytes...
174  
    }
174  
    }
175  
    @endcode
175  
    @endcode
176  
*/
176  
*/
177  
template<typename A, typename... Types>
177  
template<typename A, typename... Types>
178  
concept awaitable_decomposes_to = requires {
178  
concept awaitable_decomposes_to = requires {
179  
    typename detail::awaitable_return_t<A>;
179  
    typename detail::awaitable_return_t<A>;
180  
} && decomposes_to<detail::awaitable_return_t<A>, Types...>;
180  
} && decomposes_to<detail::awaitable_return_t<A>, Types...>;
181  

181  

182  
} // namespace capy
182  
} // namespace capy
183  
} // namespace boost
183  
} // namespace boost
184  

184  

185  
#endif
185  
#endif