2

This question was kind of asked before, but I'm not sure a satisfactory response was really offered. For me, I'm not interested in landing in a std::vector of std::string, per se, but rather a std::tuple.

For instance, if I've got std::vector<A>, std::vector<B>, and std::vector<C>, then I expect perhaps std::vector<std::tuple<A, B, C>>. Or, even std::set<std::tuple<A, B, C>>, if that was more appropriate.

Now, I could encode nested for loops, however, I'd like to do this via functions, template functions if possible, then I suppose variadic would be necessary to accomplish the task.

There are no guarantees that A, B, or C have anything to do with each other, much less conversion to std::string, as were proposed in a couple of the responses.

I want to say there could be a variadic solution to that, but I'm not exactly sure how to compose the std::vector<T> or std::vector<T>::value_type definitions.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
mwpowellhtx
  • 351
  • 1
  • 9

1 Answers1

3

If you want to compute the Cartesian product of heterogeneous vectors, you may do something like:

template <std::size_t N>
bool increase(const std::array<std::size_t, N>& sizes, std::array<std::size_t, N>& it)
{
    for (std::size_t i = 0; i != N; ++i) {
        const std::size_t index = N - 1 - i;
        ++it[index];
        if (it[index] >= sizes[index]) {
            it[index] = 0;
        } else {
            return true;
        }
    }
    return false;
}

template <typename F, std::size_t ... Is, std::size_t N, typename Tuple>
void apply_impl(F&& f,
                std::index_sequence<Is...>,
                const std::array<std::size_t, N>& it,
                const Tuple& tuple)
{
    f(std::get<Is>(tuple)[it[Is]]...);
}

template <typename F, typename ... Ts>
void iterate(F&& f, const std::vector<Ts>&... vs)
{
    constexpr std::size_t N = sizeof...(Ts);
    std::array<std::size_t, N> sizes{{vs.size()...}};
    std::array<std::size_t, N> it{{(vs.size(), 0u)...}};

    do {
        apply_impl(f, std::index_sequence_for<Ts...>(), it, std::tie(vs...));
    } while (increase(sizes, it));
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • The index vectors could become index arrays, as size is fixed and known at compile time. – Yakk - Adam Nevraumont Dec 22 '17 at 00:59
  • Interesting, so I wasn't too clear on the variadic expansion from types into vectors. – mwpowellhtx Dec 22 '17 at 07:47
  • @Yakk: array used instead of vector. – Jarod42 Dec 22 '17 at 09:02
  • If you change vs from `const std::vector&... vs` to `Ts&&...vs` you can use both rvalue vectors as well as other STL compatible containers such as std::array and QVector (tested with gcc 4.8.5). Although I did need a `static_cast(vs.size())` for the first vs.size when I used a QVector. – jfh Sep 12 '18 at 20:13