Is it possible to invoke a method with all possible K-combinations (with repetition) of arguments passed in a tuple?
Asked Answered
B

1

3

The desired behaviour can be illustrated as follows:

void foo(int x, int y) {
    std::cout << x << " " << y << std::endl;
}

int main() {
    all_combinations<2>(foo, std::make_tuple(1, 2)); // K = 2 
    // to run: 
    // foo(1, 1)
    // foo(1, 2)
    // foo(2, 1)
    // foo(2, 2)
}
Bathysphere answered 25/9, 2016 at 14:23 Comment(3)
Do you only want to have an answer with template programming? If not, I think the std::next_permutation with { 1, 1, 2, 2 } may help.Aalst
@KenmanTsang I haven't thought about run-time approach but it looks promising...Bathysphere
@W.K. Yes, compile time approach is the best! But the runtime approach may be your workaround solution before your final solution comes outAalst
B
1

The c++14 version could look as follows:

#include <tuple>
#include <utility>
#include <iostream>
#include <initializer_list>


template <class Foo, class Tuple, size_t... Is, size_t... Is2>
int all_combinations_impl(Foo foo, Tuple t, std::index_sequence<Is...> , std::integral_constant<size_t, 0>, std::index_sequence<Is2...>) {
   foo(std::get<Is>(t)...);
   std::cout << std::endl;
   return 0;
}

template <class Foo, class Tuple, size_t... Is, size_t K, size_t... Is2>
int all_combinations_impl(Foo foo, Tuple t, std::index_sequence<Is...>, std::integral_constant<size_t, K>, std::index_sequence<Is2...> is) {
   std::initializer_list<int> all = {all_combinations_impl(foo, t, std::index_sequence<Is..., Is2>{}, std::integral_constant<size_t, K-1>{}, is)...};
   (void)all;
}

template <size_t K, class Foo, class Tuple>
void all_combinations(Foo foo, Tuple t) {
   all_combinations_impl(foo, t, std::index_sequence<>{}, std::integral_constant<size_t, K>{}, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}

int main() {
   all_combinations<2>([](auto... args) { std::forward_as_tuple((std::cout << args)...); }, std::make_tuple("1 ", "2 ", "3 "));
}

Unfortunately c++11 does not come with std::integer_sequence, so we need to do additional implementation:

#include <tuple>
#include <utility>
#include <iostream>
#include <initializer_list>

template <class T, T... Vs>
struct integer_sequence { };

template <class T, class, class, class = integer_sequence<T>, class = integer_sequence<T, 0>, class = void>
struct make_integer_sequence_impl;

template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 0>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, typename std::enable_if<(ICV1 > 0)>::type>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Res...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };

template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 1>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, void>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Pow..., (Res + sizeof...(Pow))...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };

template <class T, class Res, class Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, 0>, std::integral_constant<T, 0>, Res, Pow, void> {
   using type = Res;
};

template <class T, T V>
using make_integer_sequence = typename make_integer_sequence_impl<T, std::integral_constant<T, V/2>, std::integral_constant<T, V%2>>::type;

template <size_t V>
using make_index_sequence = make_integer_sequence<size_t, V>;

template <size_t... V>
using index_sequence = integer_sequence<size_t, V...>;

template <class Foo, class Tuple, size_t... Is, size_t... Is2>
int all_combinations_impl(Foo foo, Tuple t, index_sequence<Is...> , std::integral_constant<size_t, 0>, index_sequence<Is2...>) {
   foo(std::get<Is>(t)...);
   std::cout << std::endl;
   return 0;
}

template <class Foo, class Tuple, size_t... Is, size_t K, size_t... Is2>
int all_combinations_impl(Foo foo, Tuple t, index_sequence<Is...>, std::integral_constant<size_t, K>, index_sequence<Is2...> is) {
   std::initializer_list<int> all = {all_combinations_impl(foo, t, index_sequence<Is..., Is2>{}, std::integral_constant<size_t, K-1>{}, is)...};
   (void)all;
}

template <size_t K, class Foo, class Tuple>
void all_combinations(Foo foo, Tuple t) {
   all_combinations_impl(foo, t, index_sequence<>{}, std::integral_constant<size_t, K>{}, make_index_sequence<std::tuple_size<Tuple>::value>{});
}

struct s {
   template <class... Args>
   void operator()(Args... args) const {
      std::forward_as_tuple((std::cout << args)...);
   }
};

int main() {
   all_combinations<3>(s{}, std::make_tuple("1 ", "2 ", "3 "));
}
Bathysphere answered 25/9, 2016 at 14:23 Comment(6)
Use tuple size to deduce K? Replace the sequence that always has one element with integral_constant?Tanker
Actually K can't be deduced from tuple size as even for tuple<int, int>{1, 2} K can be 3 to invoke: foo(1, 1, 1), foo(1, 1, 2), ...Bathysphere
As a minor thing index_sequence is shorter than typing the size t type. I usually make a template<std::size_t i>using index=std::integral_constant<std::size_t,i>; to make everything even a bit cleaner.Tanker
yes I did the index_sequence alias before but I must have did some typo cause it didn't compile stating Is... deduction failure. Now I cannot reproduce it so I'll make an edit...Bathysphere
` all_combinations_impl` needs return 0; no? And I'd replace the auto with std::initializer_list<int> for my own sanity, and the fact that auto x={blah...}; has some standard flux on it.Tanker
You're eye is better than -Wpedantic compiler switch :)Bathysphere

© 2022 - 2024 — McMap. All rights reserved.