LCOV - code coverage report
Current view: top level - boost/capy/concept - decomposes_to.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 0.0 % 2 0
Test Date: 2026-01-30 23:43:15 Functions: 0.0 % 6 0

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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            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