#include <array>
#include <iostream>
#include <functional>
#include <tuple>
#include <type_traits>

/* C++17 std::apply */

template <typename Fn, typename Tuple, size_t... Is>
decltype(auto) apply_impl(Fn &&fn, Tuple &&tuple, std::index_sequence<Is...>) {
  return std::forward<Fn>(fn)(std::get<Is>(std::forward<Tuple>(tuple))...);
}

template <typename Fn, typename Tuple>
decltype(auto) apply(Fn &&fn, Tuple &&tuple) {
  return apply_impl(
      std::forward<Fn>(fn),
      std::forward<Tuple>(tuple),
      std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}

/* function_arity */

template <typename F>
struct function_arity;

template <typename R, typename... Args>
struct function_arity<R (Args...)>
    : std::integral_constant<std::size_t, sizeof...(Args)> {};

template <typename R, typename... Args>
struct function_arity<R (*)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename... Args>
struct function_arity<R (&)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...) const> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...)> : function_arity<R (Args...)> {};

template <typename C>
struct function_arity : function_arity<decltype(&C::operator())> {};

/* make_integer_range */

template <typename T, typename U, T Begin>
struct make_integer_range_impl;

template <typename T, T... Ints, T Begin>
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> {
  using type = std::integer_sequence<T, Begin + Ints...>;
};

template <class T, T Begin, T End>
using make_integer_range =
    typename make_integer_range_impl<T,
                                     std::make_integer_sequence<T, End - Begin>,
                                     Begin>::type;

template <std::size_t Begin, std::size_t End>
using make_index_range = make_integer_range<std::size_t, Begin, End>;

/* slice */

template <std::size_t... Is, std::size_t... Js>
constexpr decltype(auto) slice_impl(std::index_sequence<Is...>,
                                    std::index_sequence<Js...>) {
  using array_t = std::array<std::size_t, sizeof...(Is)>;
  return std::index_sequence<std::get<Js>(array_t{{Is...}})...>();
}

template <std::size_t Begin, std::size_t End, std::size_t... Is>
constexpr decltype(auto) slice(std::index_sequence<Is...> is) {
  return slice_impl(is, make_index_range<Begin, End>());
}

/* partial_sum */

template <std::size_t... Is>
struct partial_sum;

template <std::size_t... Is>
using partial_sum_t = typename partial_sum<Is...>::type;

template <>
struct partial_sum<> { using type = std::index_sequence<>; };

template <std::size_t I, std::size_t... Is>
struct partial_sum<I, Is...> {

  template <typename Js>
  struct impl;

  template <std::size_t... Js>
  struct impl<std::index_sequence<Js...>> {
    using type = std::index_sequence<I, Js + I...>;
  };

  using type = typename impl<partial_sum_t<Is...>>::type;
};

/* split_and_call */

template <typename... Fns, typename Args, std::size_t... Is, std::size_t... Js>
void split_and_call_impl(Args &&args,
                         std::index_sequence<Is...>,
                         std::index_sequence<Js...>) {
  int dummy[] = {
      (apply_impl(Fns{}, std::forward<Args>(args), make_index_range<Is, Js>{}),
       0)...};
  (void)dummy;
}

template <typename... Fns, typename... Args>
void split_and_call(Args &&... args) {
  auto partial_sums = partial_sum_t<0, function_arity<Fns>{}...>{};
  auto is = slice<0, sizeof...(Fns)>(partial_sums);
  auto js = slice<1, sizeof...(Fns) + 1>(partial_sums);
  split_and_call_impl<Fns...>(
      std::forward_as_tuple(std::forward<Args>(args)...), is, js);
}

int main() {
  struct F { void operator()(int x, int y) {
      std::cout << "F(" << x << ", " << y << ')' << std::endl;
    }
  };
  struct G {
    void operator()(int x) { std::cout << "G(" << x << ')' << std::endl; }
  };
  split_and_call<F, G>(1, 2, 3);
}