1

I have the following snippet of code:

template <size_t N>
foo make() { ... }

...

for (...) {
    auto foo = make<1>();
    // lots of tests involving foo
}

I would like to repeat that latter block with different values for the non-type template parameter to make, e.g., make<2>, make<3>, etc.

If this was a type I wanted to iterate over, I could use this solution, but it isn't clear that I can use non-type template parameters with a generic lambda in the same way.

How can I factor this so I can create run the block of code above but instantiating make with different values for its non-type parameter. The solution should use only block-scope elements - this is easy if I can create some top-level template <size_t N> struct maker { ... } object to wrap make.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • So, do you have a terminating index? And are you sure you want to start at 1 rather than 0? – Nicol Bolas Nov 07 '19 at 01:56
  • @NicolBolas - in this case I want to start at 1, but if a solution with 0 is much cleaner I can adapt to that. Terminating index is ~10, but I am also OK listing the cases by hand, like `something { bar<1>, bar<2>, ... }` or whatever (but not duplicating all the code within the `for` loop). – BeeOnRope Nov 07 '19 at 02:09

3 Answers3

2

In such kind of situation, the right tool is to use std::integer_sequence

#include <iostream>
#include <utility>

template <size_t N>
void make()
{
  std::cout << N << std::endl;
}

template <size_t... I>
void do_make_helper(std::index_sequence<I...>)
{
  (make<I+1>(), ...); 
}

template <std::size_t N>
void do_make()
{
  do_make_helper(std::make_index_sequence<N>());
}

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

prints

1
2
3
4
5
6
7
8
9
10
Picaud Vincent
  • 10,518
  • 5
  • 31
  • 70
1

As a start point with handcrafted index list:

template <size_t N> int make();

template<> int make<1>() { std::cout<< "First" << std::endl; return 100; }
template<> int make<2>() { std::cout << "Second" << std::endl; return 200; }
template<> int make<3>() { std::cout << "Third" << std::endl; return 100; }

struct ExecuteInOrder { ExecuteInOrder( ... ) {} };

template < typename T>
void CallTest(T t )
{
    std::cout << "Your test code goes here...: " << t << std::endl;
}

    template< size_t ... N >
void Do()
{
    ExecuteInOrder {(CallTest(make<N>()),1)...};
}

int main()
{
    Do<1,2,3>();
}

or you can simply make it recursive and use index first and last like this:

template < size_t FIRST, size_t LAST >
void ExecuteAndTest()
{
    auto foo = make<FIRST>();
    std::cout << "Here we go with the test" << foo << std::endl;

    // go for next step
    if constexpr ( LAST != FIRST ) { ExecuteAndTest<FIRST+1, LAST>(); }
}

int main()
{
    // first and last index of integer sequence
    ExecuteAndTest<1,3>();
}

and finally with always from 1 to N

    template < size_t FIRST, size_t LAST >
void ExecuteAndTest_Impl()
{   
    auto foo = make<FIRST>();
    std::cout << "Here we go with the test" << foo << std::endl;

    // go for next step
    if constexpr ( LAST!= FIRST) { ExecuteAndTest_Impl<FIRST+1, LAST>(); }
}

    template < size_t LAST > 
void ExecuteAndTest()
{   
    ExecuteAndTest_Impl<1,LAST>();
}   

int main()
{   
    // or always start with 1 to n inclusive
    ExecuteAndTest<3>();
}  
Klaus
  • 24,205
  • 7
  • 58
  • 113
0

You can try this:

#include <utility>
#include <cassert>

struct Foo
{ 
    Foo() {}
    Foo(std::size_t i) : i(i) {}
    std::size_t i;
};

template <std::size_t... Is>
void setFoo(std::size_t i, Foo& foo, std::index_sequence<Is...>)
{
    ((i == Is && (foo = Foo{Is}, false)), ...);
}

int main()
{
    for (std::size_t i = 0; i < 10; i++)
    {
        Foo foo;
        setFoo(i, foo, std::make_index_sequence<10>{});
        assert(foo.i == i);
    }
}
康桓瑋
  • 33,481
  • 5
  • 40
  • 90