Search code examples
c++tuplesvariadic-templates

Slicing a tuple of vectors into a tuple of std::optional(s)


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


Solution

  • 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.