LCOV - code coverage report
Current view: top level - include/boost/http/server/detail - dynamic_invoke.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 88.5 % 26 23
Test Date: 2026-02-09 01:37:05 Functions: 97.0 % 33 32

            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            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            0 :     make_route_next()
     162              :     {
     163              :         co_return route_next;
     164            0 :     }
     165              : 
     166              :     static route_task
     167            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            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            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