TrentP
TrentP

Reputation: 4682

Convert multiple arrays into tuple

Is it possible to create a template that will concatenate a variable number of arrays into a single tuple? Or for that matter, to initialize any aggregate or construct an object. Or as arguments to a function call, which upon having a tuple, would easily be achieved with std::apply. Somewhat like Python's splat operator, but used more than one time for a single object.

I'm aware of various constructs with std::index_sequence. And I can come up with a way to deduce more than one index sequences from one than one array, but only for a fixed number of arrays. Example, for two arrays:

// Construct tuple from elements of two arrays and index sequences, giving the
// elements the corresponding array to use.
template <typename T1, size_t N1, size_t... IDX1s,
          typename T2, size_t N2, size_t... IDX2s>
auto _arrays_to_tuple(const std::array<T1, N1>& a1, std::index_sequence<IDX1s...>,
                      const std::array<T2, N2>& a2, std::index_sequence<IDX2s...>) {
    return std::tuple{ a1[IDX1s]..., a2[IDX2s]... };
}

// Call _arrays_to_tuple() with index sequences for every element in the arrays
template <typename T1, size_t N1,
          typename T2, size_t N2>
auto arrays_to_tuple(const std::array<T1, N1>& a1,
                     const std::array<T2, N2>& a2) {
    return _arrays_to_tuple(a1, std::make_index_sequence<N1>{},
                            a2, std::make_index_sequence<N2>{});
}

void test(void) {
    std::array<int, 1> i{1};
    std::array<float, 2> f{0.1, 0.2};

    std::tuple<int, float, float> x { arrays_to_tuple(i, f) };
    extern void foo(int, float, float);
    std::apply(foo, arrays_to_tuple(i, f));  // like Python splat: foo(*i, *f)
}

It's straightforward to add another template parameter for a type to be constructed in place of std::tuple.

But what if don't want a fixed number of arrays? Code like this:

std::array<int, 1> i{1};
std::array<float, 2> f{0.1, 0.2};
std::array<short, 3> s{4,5,6};

arrays_to_tuple(i);
arrays_to_tuple(i, f);
arrays_to_tuple(i, f, s);

Is it possible to create such a function in the general case, for any number of arrays?

Upvotes: 0

Views: 293

Answers (1)

n314159
n314159

Reputation: 5075

The stl has a nice function named std::tuple_cat:

#include <tuple>
#include <array>

template <typename T, size_t N, size_t... IDXs>
auto _array_to_tuple(const std::array<T, N>& a, std::index_sequence<IDXs...>) {
    return std::tuple{ a[IDXs]... };
}

template <typename T, size_t N>
auto array_to_tuple(const std::array<T, N>& a) {
    return _array_to_tuple(a, std::make_index_sequence<N>{});
}


template<class... Args>
auto arrays_to_tuple(Args&&... args) {
    return std::tuple_cat(array_to_tuple(args)...);
}

int main () {
    std::array<int, 1> i{1};
    std::array<float, 2> f{0.1, 0.2};
    std::array<short, 3> s{4,5,6};

    std::tuple<int> t1 = arrays_to_tuple(i);
    std::tuple<int, float, float> t2 = arrays_to_tuple(i, f);
    std::tuple<int, float, float, short, short, short> t3 =  arrays_to_tuple(i, f, s);

}

PS: The standard tells us, that

An implementation may support additional types in the template parameter pack Tuples that support the tuple-like protocol, such as pair and array.

So just throwing the arrays directly into it can work (it does for me on gcc 9.2.0 and clang 9.0.0) but is undefined behavior.

PPS: Yes, I cheated ;-P

Upvotes: 2

Related Questions