I've tried to put together a non-recursive instantiating version, but it involves a few utilities that don't currently exist.
split_and_call
Suppose we have F
which takes 2 int
s, and G
that takes 1 int
and arguments 1, 2, 3
.
Given F
, G
, tuple(1, 2, 3)
, index_sequence<0, 1>
, index_sequence<2>
, we want to call apply_impl(F{}, tuple(1, 2, 3), index_sequence<0, 1>{})
and apply_impl(G{}, tuple(1, 2, 3), index_sequence<2>{})
.
Expanding the F
, G
is simple with Fns{}...
, and making the tuple of arguments is also simple with std::forward_as_tuple(std::forward<Args>(args)...)
. We're left to construct the index_sequence
s.
Suppose our function arities are [2, 1, 3]
, we first get the partial sum of this and prepend a 0
: [0, 2, 3, 6]
.
The index ranges we want are: [0, 2)
, [2, 3)
, [3, 6)
.
We split [0, 2, 3, 6]
into is = [0, 2, 3]
, and js = [2, 3, 6]
and zip them to get the ranges we want.
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);
}
Utilities
- std::apply (C++17)
- function_arity
- make_index_range
- slice
- partial_sum
std::apply
The part we need is actually the apply_impl
part.
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))...);
}
function_arity
Used to determine the arity of a function.
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_index_range
A variation on make_index_sequence<N>
which constructs index_sequence<0, .. N>
. make_index_range<B, E>
constructs index_sequence<B, .. E>
.
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
Slices an index_sequence
in the range [Begin, End)
.
e.g. slice<0, 2>(index_sequence<2, 3, 4, 5>{}) == index_sequence<2, 3>
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
Functional version of std::partial_sum
.
e.g. partial_sum<2, 3, 4> == index_sequence<2, 5, 9>
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;
};
Full solution on Ideone
Bonus
I'll share this part since I played with this further for fun. I won't go into too much detail since it's not what was asked.
- Updated the syntax to
call(fs...)(args...);
so that top-level functions for example can be passed. e.g. call(f, g)(1, 2, 3)
- Returned the results of each of the function calls as a
std::tuple
. e.g. auto result = call(f, g)(1, 2, 3)
Full solution on Ideone
Foos
functors with each piece ? – GranuloseFoos
, all of which are functions, into the templateArgs
. Then in the function, split and call them correctly. – FerdinandferdinandaFoos
? – GranuloseFoos...
? That is, what'si
,j
,l
? is them
inargs[m-1]
the samem = sizeof...(Args)
? – PhaticFoos
needs to be answered still. SupposeF
takes 1 or 2int
s andG
takes 0, or 1. Givensplit_and_call<F, G>(1, 2);
, Do you callF(1), G(2)
, orF(1, 2), G()
? – Phaticfloat
? :) – GranuloseFoos
are all known. In the function call itself, the arguments are packed as described by the function declarations. So I suppose the answer is "whichever the compiler happened to choose." – Ferdinandferdinandainteger_sequence
s, such that each sequence contains the indices for the arguments of the corresponding Foo, and do acall(Foos, Seqs, Args...)...
, wherecall
is justFoo(get<index>(Args)...)
. – Historyadd_to_each_sequence_member<PartialSums, make_index_sequence<N>>...
is the pack of sequences. – Historytuple_cat
. Searching for how that is implemented should give you some ideas. – HistoryFoo
s as types here... did you mean to pass them as args somehow? – Diabetes