Home > database >  Slicing a vector of tuples into a tuple of std::optional
Slicing a vector of tuples into a tuple of std::optional

Time:12-20

I'm having trouble slicing a tuple.

I want to output a tuple with std::optional, so that if the vectors are of an uneven length, the tuple is padded with empty std::optional objects.

An example of my implementation is below, but I'm running into a roadblock, which is commented in the if-statements

template <typename... Types>
auto sliceTuple(std::tuple<std::vector<Types>...> &data, int index)
{

    std::tuple<std::optional<Types>...> output;

    std::apply(
        [&output, index](auto &vector)
        {
            if (index < vector.size())
            {
                //Problem here: How would I write vector[index] to the tuple? 
            }
            else
            {
                //Problem here: How would I write an empty std::optional to the tuple?
            }
        },
        data)

    return (output);
}

If std::apply had a compile-time index, it would be trivial, (I could just use std::get<i>(output); ) but as far as I know, it does not. Is there a way to implement this "slice tuple of vectors" function, and if so, how would I do so?

An example of what I want to do:

Starting with a tuple of 3 vectors:


<[1, 2], [3, 4, 5], [6, 7, 8]>

should become 

<1, 3, 6> when index = 0
<2, 4, 7> when index = 1
<empty std::optional, 5, 8> when index = 2

CodePudding user response:

template <typename... Types>
auto sliceTuple(std::tuple<std::vector<Types>...>& data, size_t index)
{
    return std::apply([index](const auto&... vs) { 
        return std::make_tuple([index](const auto& v) { 
            if (index < std::size(v))
            {
                return std::make_optional(v[index]);
            }
            return std::optional<Types>{};
        }(vs)...);
    }, data);
}

https://godbolt.org/z/T68YPxcsY

Here is version which is spitted into two parts:

  • apply tuple on lambda which constructs tuple of optionals
  • take index based optional from a range
template <typename R>
auto optional_from_range(const R& r, size_t index) -> std::optional<std::decay_t<decltype(r[index])>>
{
    if (index < std::size(r)) {
        return r[index];
    }
    return {};
}

template <typename... Types>
auto sliceTuple(std::tuple<std::vector<Types>...>& data, size_t index)
{
    return std::apply([index](const auto&... vs) { 
        return std::make_tuple(optional_from_range(vs, index)...);
    }, data);
}

https://godbolt.org/z/z7xrbr4d3

this should be easier to understand.

CodePudding user response:

With the help of std::apply and the fold-expression:

#include <optional>
#include <tuple>
#include <vector>

template <typename... Types>
auto sliceTuple(std::tuple<std::vector<Types>...>& data, int index) {
  return std::apply([index](auto&... vecs) {
    return std::tuple(
      (index < vecs.size() ? std::optional(vecs[index]) : 
                             std::nullopt)...);
  }, data);
}

Demo

  • Related