LCOV - code coverage report
Current view: top level - capy/concept - decomposes_to.hpp (source / functions) Coverage Total Missed
Test: coverage_remapped.info Lines: 0.0 % 2 2
Test Date: 2026-02-17 18:14:47 Functions: 0.0 % 34 34

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       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)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/capy
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_CAPY_DECOMPOSES_TO_HPP
      11                 : #define BOOST_CAPY_DECOMPOSES_TO_HPP
      12                 : 
      13                 : #include <boost/capy/detail/config.hpp>
      14                 : 
      15                 : #include <system_error>
      16                 : #include <concepts>
      17                 : #include <cstddef>
      18                 : #include <tuple>
      19                 : #include <type_traits>
      20                 : #include <utility>
      21                 : 
      22                 : namespace boost {
      23                 : namespace capy {
      24                 : namespace detail {
      25                 : 
      26                 : struct any_type
      27                 : {
      28                 :     template <typename T>
      29                 :     constexpr operator T() const noexcept;
      30                 : };
      31                 : 
      32                 : template <typename T, std::size_t N>
      33                 : concept is_tuple_n = requires {
      34                 :     std::tuple_size<std::remove_cvref_t<T>>::value;
      35                 : } && std::tuple_size<std::remove_cvref_t<T>>::value == N;
      36                 : 
      37                 : // clang-format off
      38                 : template <typename T>
      39                 : concept is_decomposable_1 =
      40                 :     (std::is_aggregate_v<std::remove_cvref_t<T>> &&
      41                 :         requires { std::remove_cvref_t<T>{ any_type{} }; } &&
      42                 :         !requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; }
      43                 :     ) || is_tuple_n<T, 1>;
      44                 : 
      45                 : template <typename T>
      46                 : concept is_decomposable_2 = 
      47                 :     (std::is_aggregate_v<std::remove_cvref_t<T>> &&
      48                 :         requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; } &&
      49                 :         !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; }
      50                 :     ) || is_tuple_n<T, 2>;
      51                 : 
      52                 : template <typename T>
      53                 : concept is_decomposable_3 = 
      54                 :     (std::is_aggregate_v<std::remove_cvref_t<T>> &&
      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{} }; }
      57                 :     ) || is_tuple_n<T, 3>;
      58                 : 
      59                 : template <typename T>
      60                 : concept is_decomposable_4 = 
      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{} }; } &&
      63                 :         !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{}, any_type{} }; }
      64                 :     ) || is_tuple_n<T, 4>;
      65                 : 
      66                 : // clang-format on
      67                 : 
      68                 : template <is_decomposable_1 T>
      69                 : auto decomposed_types(T&& t)
      70                 : {
      71                 :     auto [v0] = t;
      72                 :     return std::make_tuple(v0);
      73                 : }
      74                 : 
      75                 : template <is_decomposable_2 T>
      76                 : auto decomposed_types(T&& t)
      77                 : {
      78                 :     auto [v0, v1] = t;
      79                 :     return std::make_tuple(v0, v1);
      80                 : }
      81                 : 
      82                 : template <is_decomposable_3 T>
      83                 : auto decomposed_types(T&& t)
      84                 : {
      85                 :     auto [v0, v1, v2] = t;
      86                 :     return std::make_tuple(v0, v1, v2);
      87                 : }
      88                 : 
      89                 : template <is_decomposable_4 T>
      90                 : auto decomposed_types(T&& t)
      91                 : {
      92                 :     auto [v0, v1, v2, v3] = t;
      93                 :     return std::make_tuple(v0, v1, v2, v3);
      94                 : }
      95                 : 
      96                 : template <class T>
      97                 : std::tuple<> decomposed_types(T&&)
      98                 : {
      99                 :     return {};
     100                 : }
     101                 : 
     102                 : template<typename T>
     103 MIS           0 : auto get_awaiter(T&& t)
     104                 : {
     105                 :     if constexpr (requires { std::forward<T>(t).operator co_await(); })
     106                 :     {
     107                 :         return std::forward<T>(t).operator co_await();
     108                 :     }
     109                 :     else if constexpr (requires { operator co_await(std::forward<T>(t)); })
     110                 :     {
     111                 :         return operator co_await(std::forward<T>(t));
     112                 :     }
     113                 :     else
     114                 :     {
     115               0 :         return std::forward<T>(t);
     116                 :     }
     117                 : }
     118                 : 
     119                 : template<typename A>
     120                 : using awaitable_return_t = decltype(
     121                 :     get_awaiter(std::declval<A>()).await_resume()
     122                 : );
     123                 : 
     124                 : } // namespace detail
     125                 : 
     126                 : /** Concept for types that decompose to a specific typelist.
     127                 : 
     128                 :     A type satisfies `decomposes_to` if it can be decomposed via
     129                 :     structured bindings into the specified types. This includes
     130                 :     aggregates with matching member types and tuple-like types
     131                 :     with matching element types.
     132                 : 
     133                 :     @tparam T The type to decompose.
     134                 :     @tparam Types The expected element types after decomposition.
     135                 : 
     136                 :     @par Example
     137                 :     @code
     138                 :     struct result { int a; double b; };
     139                 : 
     140                 :     static_assert(decomposes_to<result, int, double>);
     141                 :     static_assert(decomposes_to<std::tuple<int, double>, int, double>);
     142                 :     @endcode
     143                 : */
     144                 : template <typename T, typename... Types>
     145                 : concept decomposes_to = requires(T&& t) {
     146                 :     { detail::decomposed_types(std::forward<T>(t)) } -> std::same_as<std::tuple<Types...>>;
     147                 : };
     148                 : 
     149                 : /** Concept for awaitables whose return type decomposes to a specific typelist.
     150                 : 
     151                 :     A type satisfies `awaitable_decomposes_to` if it is an awaitable
     152                 :     (has `await_resume`) and its return type decomposes to the
     153                 :     specified typelist.
     154                 : 
     155                 :     @tparam A The awaitable type.
     156                 :     @tparam Types The expected element types after decomposition.
     157                 : 
     158                 :     @par Requirements
     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...`
     161                 : 
     162                 :     @par Example
     163                 :     @code
     164                 :     // Constrain a function to accept only awaitables that return
     165                 :     // a decomposable result of (error_code, size_t)
     166                 :     template<typename A>
     167                 :         requires awaitable_decomposes_to<A, std::error_code, std::size_t>
     168                 :     task<void> process(A&& op)
     169                 :     {
     170                 :         auto [ec, n] = co_await std::forward<A>(op);
     171                 :         if (ec)
     172                 :             co_return;
     173                 :         // process n bytes...
     174                 :     }
     175                 :     @endcode
     176                 : */
     177                 : template<typename A, typename... Types>
     178                 : concept awaitable_decomposes_to = requires {
     179                 :     typename detail::awaitable_return_t<A>;
     180                 : } && decomposes_to<detail::awaitable_return_t<A>, Types...>;
     181                 : 
     182                 : } // namespace capy
     183                 : } // namespace boost
     184                 : 
     185                 : #endif
        

Generated by: LCOV version 2.3