Search code examples
c++c++11template-meta-programminggeneric-programming

Given a tuple with arbitrary number of vectors holding different types how do I extract the vector with minimum size?


In my application I generate a tuple with many vectors holding values. I would like to have a generic way of iterating over the tuple to extract the single vector with least values.

For example:

auto t = std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0}));

How do I extract the vector which holds the least values?


Solution

  • Let's assume we either have no duplicates in our pack of types, or a way of removing them, and also a C++11 backport of std::index_sequence

    #include <map>
    #include <tuple>
    #include <functional>
    #include <iostream>
    
    #include <boost/variant>
    #include <future/index_sequence>
    
    namespace detail {
        template<typename Variant, typename Tuple, std::size_t... Is>
        std::map<std::size_t, std::function<Variant(const Tuple &)>> from_tuple_map(index_sequence<Is...>)
        {
            return { { Is, [](const Tuple & tup){ return std::get<Is>(tup); } }... };
        }
    
        struct GetSize
        {
            template<typename T>
            std::size_t operator()(const std::vector<T> & vec){ return vec.size(); }
        }; // becomes C++14 generic lambda
    }
    
    template<typename... Ts>
    boost::variant<Ts...> from_tuple(const std::tuple<Ts...> & tup)
    {
        auto map = detail::from_tuple_map<boost::variant<Ts...>, std::tuple<Ts...>>(make_index_sequence<sizeof...(Ts)>());
        auto get_size = GetSize{};
        std::size_t best = 0;
        std::size_t len = visit(get_size, map[best](tup));
        for (std::size_t trial = 1; trial < sizeof...(Ts); ++trial)
        {
            std::size_t trial_len = visit(get_size, map[trial](tup));
            if (trial_len > len)
            {
                best = trial;
                len = trial_len;
            }
        }
        return map[best](tup);
    }
    
    int main()
    {
        auto x = from_tuple(std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0})));
        visit([](const auto & a){ std::cout << a[1]; }, x);
    }
    

    See it live (using C++17 compiler)