c++templatestuplesstdtemplate-meta-programming

Iterate over types of tuple in C++


I want to iterate over types of a tuple, not its elements.
Imagine you have a generic, base-class interface Controller and you want to have a vector of pointers (I use raw pointer instead of smart, for readability): std::vector<Controller*> ctrls;

Now you want to add many implementations of Controller interface to this vector, so you can do this:

ctrls.emplace_back(new MouseCtrl());
ctrls.emplace_back(new KeyboardCtrl());
ctrls.emplace_back(new ScreenCtrl());
(... and so on)

But this is ugly and not exactly extendable (as in Open-Close Principle), so it would be better to have, for example, a tuple: using Controllers = std::tuple<MouseCtrl, KeyboardCtrl, ScreenCtrl>; and then, in the some init function, iterate over those types:

for (T : Controllers> { 
   ctrls.emplace_back(new T());
}

Obviously, the code above it's not valid C++ syntax. So the question is: how to do this?. I've looked both into std::apply and std::visit / std::variant, but I don't have idea how to do this (they iterate over elements, not types).


Solution

  • You can use std::tuple_size and std::tuple_element to get the type of every tuple element (this will put it in reverse order, but with a little modification you can revert the order):

    #include <iostream>
    #include <variant>
    #include <vector>
    
    using namespace std;
    
    using Controllers = tuple<int, char, float>;
    using ControllersContainer = vector<variant<int, char, float>>;
    
    template <size_t N>
    void add(ControllersContainer& ctrls)
    {
        ctrls.emplace_back(tuple_element_t<N-1, Controllers>{});
        add<N - 1>(ctrls);
    }
    
    template <>
    void add<0>(ControllersContainer& ctrls)
    {
        ctrls.emplace_back(tuple_element_t<0, Controllers>{});
    }
    
    int main()
    {
        ControllersContainer ctrls;
        add<tuple_size_v<Controllers>>(ctrls);
    }