LCOV - code coverage report
Current view: top level - include/boost/http/server/detail - dynamic_invoke.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 88.5 % 26 23 3
Test Date: 2026-06-13 19:44:58 Functions: 97.0 % 33 32 1

           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/http
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
      11                 : #define BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
      12                 : 
      13                 : #include <boost/http/detail/config.hpp>
      14                 : #include <boost/http/core/polystore.hpp>
      15                 : #include <boost/http/server/route_handler.hpp>
      16                 : #include <tuple>
      17                 : #include <type_traits>
      18                 : #include <utility>
      19                 : 
      20                 : namespace boost {
      21                 : namespace http {
      22                 : namespace detail {
      23                 : 
      24                 : //------------------------------------------------
      25                 : 
      26                 : template<class... Ts>
      27                 : struct are_unique : std::true_type {};
      28                 : 
      29                 : template<class T, class... Ts>
      30                 : struct are_unique<T, Ts...>
      31                 :     : std::bool_constant<
      32                 :         (!std::is_same_v<T, Ts> && ...) &&
      33                 :         are_unique<Ts...>::value> {};
      34                 : 
      35                 : //------------------------------------------------
      36                 : 
      37                 : template<class T>
      38                 : using find_key_t =
      39                 :     std::remove_cv_t<
      40                 :         std::remove_reference_t<T>>;
      41                 : 
      42                 : // Polystore lookup key: also strips pointer
      43                 : template<class T>
      44                 : using lookup_key_t =
      45                 :     std::remove_cv_t<
      46                 :         std::remove_pointer_t<
      47                 :             find_key_t<T>>>;
      48                 : 
      49                 : // True when parameter is a pointer (optional dependency)
      50                 : template<class T>
      51                 : constexpr bool is_optional_v =
      52                 :     std::is_pointer_v<find_key_t<T>>;
      53                 : 
      54                 : // Resolve a polystore pointer to the handler's expected arg
      55                 : template<class Arg, class T>
      56 HIT           3 : auto resolve_arg(T* p)
      57                 : {
      58                 :     if constexpr (is_optional_v<Arg>)
      59               2 :         return static_cast<find_key_t<Arg>>(p);
      60                 :     else
      61               1 :         return static_cast<Arg>(*p);
      62                 : }
      63                 : 
      64                 : //------------------------------------------------
      65                 : 
      66                 : template<class F, class... Args>
      67                 : route_result
      68                 : dynamic_invoke_impl(
      69                 :     polystore& ps,
      70                 :     F const& f,
      71                 :     type_list<Args...> const&)
      72                 : {
      73                 :     static_assert(
      74                 :         are_unique<lookup_key_t<Args>...>::value,
      75                 :         "callable has duplicate parameter types");
      76                 : 
      77                 :     auto ptrs = std::make_tuple(
      78                 :         ps.find<lookup_key_t<Args>>()...);
      79                 : 
      80                 :     return [&]<std::size_t... I>(
      81                 :         std::index_sequence<I...>) -> route_result
      82                 :     {
      83                 :         if constexpr (!(is_optional_v<Args> && ...))
      84                 :         {
      85                 :             if(! (... && (is_optional_v<Args> ||
      86                 :                 std::get<I>(ptrs) != nullptr)))
      87                 :                 return route_next;
      88                 :         }
      89                 :         return f(resolve_arg<Args>(
      90                 :             std::get<I>(ptrs))...);
      91                 :     }(std::index_sequence_for<Args...>{});
      92                 : }
      93                 : 
      94                 : /** Invoke a callable, resolving arguments from a polystore.
      95                 : 
      96                 :     Each parameter type of the callable is looked up in the
      97                 :     polystore via @ref polystore::find. If all required
      98                 :     parameters are found, the callable is invoked with the
      99                 :     resolved arguments and its result is returned. If any
     100                 :     required parameter is not found, @ref route_next is
     101                 :     returned without invoking the callable.
     102                 : 
     103                 :     Parameters declared as pointer types (e.g. `A*`) are
     104                 :     optional: `nullptr` is passed when the type is absent.
     105                 :     Rvalue reference parameters (e.g. `A&&`) are supported
     106                 :     and receive a moved reference to the stored object.
     107                 : 
     108                 :     Duplicate parameter types (after stripping cv-ref and
     109                 :     pointer) produce a compile-time error.
     110                 : 
     111                 :     @param ps The polystore to resolve arguments from.
     112                 :     @param f The callable to invoke.
     113                 :     @return The result of the callable, or @ref route_next
     114                 :     if any required parameter was not found.
     115                 : */
     116                 : template<class F>
     117                 : route_result
     118                 : dynamic_invoke(
     119                 :     polystore& ps,
     120                 :     F const& f)
     121                 : {
     122                 :     return dynamic_invoke_impl(
     123                 :         ps, f,
     124                 :         typename call_traits<
     125                 :             std::decay_t<F>>::arg_types{});
     126                 : }
     127                 : 
     128                 : //------------------------------------------------
     129                 : 
     130                 : /** Wraps a callable whose first parameter is `route_params&`
     131                 :     and whose remaining parameters are resolved from
     132                 :     @ref route_params::route_data at dispatch time.
     133                 : 
     134                 :     Produced by @ref dynamic_transform. Stored inside
     135                 :     the router's handler table.
     136                 : */
     137                 : template<class F>
     138                 : struct dynamic_handler
     139                 : {
     140                 :     F f;
     141                 : 
     142                 :     // No extra parameters -- just forward to the callable
     143                 :     template<class First>
     144                 :     route_task
     145               3 :     invoke_impl(
     146                 :         route_params& p,
     147                 :         type_list<First> const&) const
     148                 :     {
     149                 :         static_assert(
     150                 :             std::is_convertible_v<route_params&, First>,
     151                 :             "first parameter must accept route_params&");
     152                 :         using R = std::invoke_result_t<
     153                 :             F const&, route_params&>;
     154                 :         if constexpr (std::is_same_v<R, route_task>)
     155                 :             return f(p);
     156                 :         else
     157               3 :             return wrap_result(f(p));
     158                 :     }
     159                 : 
     160                 :     static route_task
     161 MIS           0 :     make_route_next()
     162                 :     {
     163                 :         co_return route_next;
     164               0 :     }
     165                 : 
     166                 :     static route_task
     167 HIT           6 :     wrap_result(route_result r)
     168                 :     {
     169                 :         co_return r;
     170              12 :     }
     171                 : 
     172                 :     // Extra parameters resolved from route_data
     173                 :     template<class First, class E1, class... Extra>
     174                 :     route_task
     175               3 :     invoke_impl(
     176                 :         route_params& p,
     177                 :         type_list<First, E1, Extra...> const&) const
     178                 :     {
     179                 :         static_assert(
     180                 :             std::is_convertible_v<route_params&, First>,
     181                 :             "first parameter must accept route_params&");
     182               3 :         return invoke_extras(p, type_list<E1, Extra...>{});
     183                 :     }
     184                 : 
     185                 :     template<class... Extras>
     186                 :     route_task
     187               3 :     invoke_extras(
     188                 :         route_params& p,
     189                 :         type_list<Extras...> const&) const
     190                 :     {
     191                 :         static_assert(
     192                 :             are_unique<
     193                 :                 lookup_key_t<Extras>...>::value,
     194                 :             "callable has duplicate parameter types");
     195                 : 
     196               3 :         auto ptrs = std::make_tuple(
     197                 :             p.route_data.template find<
     198               3 :                 lookup_key_t<Extras>>()...);
     199                 : 
     200               6 :         return [this, &p, &ptrs]<std::size_t... I>(
     201                 :             std::index_sequence<I...>) -> route_task
     202                 :         {
     203                 :             if constexpr (!(is_optional_v<Extras> && ...))
     204                 :             {
     205               1 :                 if(! (... && (is_optional_v<Extras> ||
     206               1 :                     std::get<I>(ptrs) != nullptr)))
     207 MIS           0 :                     return make_route_next();
     208                 :             }
     209                 : 
     210                 :             using R = std::invoke_result_t<
     211                 :                 F const&, route_params&, Extras...>;
     212                 :             if constexpr (std::is_same_v<R, route_task>)
     213                 :                 return f(p, resolve_arg<Extras>(
     214                 :                     std::get<I>(ptrs))...);
     215                 :             else
     216 HIT           3 :                 return wrap_result(f(p,
     217               2 :                     resolve_arg<Extras>(
     218               4 :                         std::get<I>(ptrs))...));
     219               6 :         }(std::index_sequence_for<Extras...>{});
     220                 :     }
     221                 : 
     222                 :     route_task
     223               6 :     operator()(route_params& p) const
     224                 :     {
     225                 :         return invoke_impl(p,
     226                 :             typename call_traits<
     227               6 :                 std::decay_t<F>>::arg_types{});
     228                 :     }
     229                 : };
     230                 : 
     231                 : /** A handler transform that resolves extra parameters from route_data.
     232                 : 
     233                 :     When used with @ref router::with_transform, handlers may
     234                 :     declare a first parameter of type `route_params&` followed
     235                 :     by additional parameters of arbitrary types. At dispatch time,
     236                 :     each extra parameter type is looked up in
     237                 :     @ref route_params::route_data via @ref polystore::find.
     238                 :     If all required parameters are found the handler is invoked;
     239                 :     otherwise @ref route_next is returned.
     240                 : 
     241                 :     Parameters declared as pointer types (e.g. `A*`) are
     242                 :     optional: `nullptr` is passed when the type is absent.
     243                 :     Rvalue reference parameters (e.g. `A&&`) are supported
     244                 :     and receive a moved reference to the stored object.
     245                 : 
     246                 :     Duplicate extra parameter types (after stripping cv-ref
     247                 :     and pointer) produce a compile-time error.
     248                 : 
     249                 :     @par Example
     250                 :     @code
     251                 :     router<route_params> base;
     252                 :     auto r = base.with_transform( dynamic_transform{} );
     253                 : 
     254                 :     r.get( "/users", [](
     255                 :         route_params& p,
     256                 :         UserService& svc,
     257                 :         Config const& cfg) -> route_result
     258                 :     {
     259                 :         // svc and cfg resolved from p.route_data
     260                 :         return route_done;
     261                 :     });
     262                 :     @endcode
     263                 : */
     264                 : struct dynamic_transform
     265                 : {
     266                 :     template<class F>
     267                 :     auto
     268               6 :     operator()(F f) const ->
     269                 :         dynamic_handler<std::decay_t<F>>
     270                 :     {
     271               6 :         return { std::move(f) };
     272                 :     }
     273                 : };
     274                 : 
     275                 : } // detail
     276                 : } // http
     277                 : } // boost
     278                 : 
     279                 : #endif
        

Generated by: LCOV version 2.3