Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over template classes in c++ 11

Suppose, I have class with such definition:

template<unsigned int N>
class A { ... }

The question is how to iterate over this classes with N?

for(unsigned int i = 0; i < 10; ++i) {
    A<i>().doStuff();
}

Maybe there is some new feature in C++ 11 or some cool using of contrexp .

And next question is: if it's possible - how to store such classes?

Update I know that it works at compile time. Suppose, I have up to 10 such global classes, which differs only in N. For example:

A<1> first;
A<2> second;
A<42> third;
A<1034> fourth;

And suppose, I should call the one who's got N bigger than my value. If there is no chances to iterate, so I have to write long if-else structure.

void doAppropriateStuff(int value) {
    if (value < 1) {
        first.doStuff();
    } else if (value < 2) {
        second.doStuff();
    } else if (value < 42) {
        third.doStuff();
    } else if (value < 1034) {
        fourth.doStuff();
    } else {
      ...
    }
}

Hope, the problem became clearer. As I googled that's impossible and I understand why. Only hopes on C++11 and SO community. Thanks.

like image 434
htzfun Avatar asked Mar 25 '14 17:03

htzfun


3 Answers

It's obviously impossible with a for loop, because that's ran at runtime and template arguments need to be compile time constants. Here's how you could do it.

These are utility classes for constructing a sequence of integers as a template argument pack:

template< std::size_t... Ns >
struct indices {
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices {
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 > {
    typedef indices<> type;
};

The function that does the work:

#include <initializer_list>

template<size_t... Is>
void foo(indices<Is...>)
{
    auto list = { (A<Is>().doStuff(), 0)... };
}

And you call the function like this:

foo(make_indices<10>::type());
like image 158
jrok Avatar answered Nov 12 '22 08:11

jrok


If you do not want to rely on integer_sequence that is c++14, this is a simpler solution :

#include <iostream>

template<unsigned int N>
struct A {
    void dostuff() const { std::cout << N << " "; }
};

template < int N > void run();

template <>        void run<-1>() {}
template < int N > void run() {
    run<N-1>();
    A<N>{}.dostuff();
}

int main() {
    run<10>();
}

EDIT : about your question update, you can do that if you store the objects inside a tuple, see here :

#include <iostream>
#include <tuple>

template<unsigned int N>
struct A {
    unsigned int getN() const { return N; }
    void dostuff() const { std::cout << N << " "; }
};

auto globals = std::make_tuple( A<3>{}, A<7>{}, A<10>{}, A<200>{} );

template <int idx> void run( int v );
template <>        void run<std::tuple_size<decltype(globals)>::value>( int ) {}
template <int idx = 0> void run( int v ) {
    auto & a = std::get<idx>(globals);
    if ( v < a.getN() ) {
        a.dostuff();
    } else {
        run<idx+1>(v);
    }
}

int main() {
    for( int i = 0; i<20; ++i)
        run( i );
}
like image 5
galop1n Avatar answered Nov 12 '22 07:11

galop1n


You would use template specialization:

template <unsigned int N> struct ADoer
{
    static void go() { A<N>().doStuff(); ADoer<N - 1>::go(); }
};

template <> struct ADoer<0>
{
    static void go() { }
};
like image 2
Kerrek SB Avatar answered Nov 12 '22 09:11

Kerrek SB