Search code examples
c++c++17variadic-templatestemplate-meta-programming

How can I select a subset of tuple types and create another tuple from contents of the subset?


I have a tuple of structs that are defined using variadic arguments:

#include <tuple>
#include <vector>

template<class Type>
struct Pool {
  std::vector<Type> components;
};

template<class... Types>
class Storage {
public:
  std::tuple<Pool<Types>...> pools;

  template<class SelectedType0, class... SelectedTypes>
  std::tuple<std::vector<SelectedTypes>&...> getVectorsFromTuple();

  template<class... SelectedTypes>
  void iterate();
};

I want a way to select subset of types from the tuple and create a new tuple that is based on contents of it. Essentially, this is what I am trying to achieve:

template<class... Types>
template<class... SelectedTypes>
void Storage<Types...>::iterate() {
  std::tuple<std::vector<SelectedTypes>&...> vectors = getVectorsFromTuple<SelectedTypes...>(pools);
  // getVectorsFromTuple should get the values in the tuple based on types
  // and save reference to each tuple item's structure's `components`
  // property in another tuple 
}

One idea that I got for it was to "define" first template parameter in another function and then use that function to recursively define other parameters:

template <class... Types>
template<class SelectedType0, class... SelectedTypes>
inline std::tuple<std::vector<SelectedType0>&, std::vector<SelectedTypes>&...> EntityStorageSparseSet<ComponentTypes...>::getVectorsFromPools() {
    return std::make_tuple(std::get<PickComponent0>(pools).components, getVectorsFromPools<PickComponents...>());
}

But I don't know how to unroll/flatten the recursed tuples so this function returns something like:

std::tuple<SelectedType0, SelectedType1, ...>

instead of

std::tuple<SelectedType0, std::tuple<SelectedType1, std::tuple<SelectedType2, ...>>>

How can I unroll the tuples or is there a better way to implement what I am trying to achieve?


Solution

  • Following seems to do the job (assuming uniqueness of type in Types):

    template <typename... Types>
    template <typename... SelectedTypes>
    std::tuple<std::vector<SelectedTypes>&...>
    EntityStorageSparseSet<Types...>::getVectorsFromPools()
    {
        return { std::get<SelectedTypes>(pools).components... };
    }