namespace seafire::common
{

  template<typename Ret, typename T, typename... Direct, typename... Params>
  Ret
  do_invoke(T& target,
            server::request_t& req,
            Direct const&... direct,
            Ret (T::*func)(Direct const&..., Params const&...))
  {
    return (target.*func)(direct..., std::decay_t<Params>::fetch(req)...);
  }

  template<typename Ret, typename T, typename... Params, typename... Direct>
  Ret
  invoke(T& target,
         server::request_t& req,
         Ret (T::*func)(Params const&...),
         Direct const&... direct)
  {
    return do_invoke<Ret, T, Direct...>(target, req, direct..., func);
  }

  template<typename Ret, typename T, typename... Direct, typename... Params>
  Ret
  do_invoke(T const& target,
            server::request_t& req,
            Direct const&... direct,
            Ret (T::*func)(Direct const&..., Params const&...) const)
  {
    return (target.*func)(direct..., std::decay_t<Params>::fetch(req)...);
  }

  template<typename Ret, typename T, typename... Params, typename... Direct>
  Ret
  invoke(T const& target,
         server::request_t& req,
         Ret (T::*func)(Params const&...) const,
         Direct const&... direct)
  {
    return do_invoke<Ret, T, Direct...>(target, req, direct..., func);
  }

  template<typename Ret, typename... Direct, typename... Params>
  Ret
  do_invoke(server::request_t& req,
            Direct&&... direct,
            Ret (*func)(Direct..., Params...))
  {
    return (*func)(direct..., std::decay_t<Params>::fetch(req)...);
  }

  template<typename Ret, typename... Params, typename... Direct>
  Ret
  invoke(server::request_t& req,
         Ret (*func)(Params...),
         Direct&&... direct)
  {
    return do_invoke<Ret, Direct...>(req, direct..., func);
  }

} // namespace seafire::common