0

I would like to call a template function foo with a sequence of different (non-type) parameters, taking consecutive integer values between a lower and upper bound. For instance:

template <int K> void foo(){ ... }

int const UPPER = 10, LOWER = 5;

for(int i = LOWER; i <= UPPER; i++)
    foo<i>();

This, of course, won't work since i is not known at compile time. I am looking for a way to achieve this sort of program without having to write something like:

foo<5>(); foo<6>(); foo<7>(); foo<8>(); foo<9>(); foo<10>();

This is in particular because I intend on changing UPPER and LOWER from one execution to the next.

My only idea was to create a constant array of the integers which will be sent to the template parameter:

int const arr[6] = {5, 6, 7, 8, 9, 10};

for(int i = LOWER; i <= UPPER; i++)
    foo<arr[i]>();

But, again, although the elements of the array are constant, i is not known at compile-time so neither is arr[i]. Any suggestions?

Thank you in advance.

2
  • 2
    It looks like you may want to use a std::integer_sequence Commented Jun 21, 2016 at 13:01
  • You might put foo() to a template<int K> struct foowrapper, that makes it a lot easier.. Is that allowed? Commented Jun 21, 2016 at 13:07

3 Answers 3

3

You can use two templates and std::enable_if to select one of them, depending on whether Lower is equal to Upper. In case they are equal, we do nothing. Otherwise, we invoke foo<Lower>() and recurse with parameters Lower + 1 and Upper.

template <int Lower, int Upper>
typename std::enable_if<Lower == Upper, void>::type callFoo()
{

}

template <int Lower, int Upper>
typename std::enable_if<Lower != Upper, void>::type callFoo()
{
    static_assert(Lower < Upper, "Lower must be less than or equal to Upper");

    foo<Lower>();
    callFoo<Lower + 1, Upper>();
}

Given this template, the following line will invoke foo<K>() for K values 5, 6, 7, 8, 9, 10.

callFoo<5, 11>();
Sign up to request clarification or add additional context in comments.

2 Comments

Very elegant solution. Thank you.
Out of curiosity, do you know of any workarounds which could make this code compatible with older versions of C++ (prior to C++11)?
3

You can make use of std::integer_sequence to get a compile-time list of numbers from 0 ascending and then add your offset:

// Here we take the lower bound and the sequence 0 to (Upper - Lower).
// We call foo with each number in the sequence added to the lower bound.
template<int Lower, int... Ints>
void call_foo_with_range_helper(std::integer_sequence<int, Ints...>) {
    // A common trick to expand the parameter pack without recursion or fold expressions
    (void)std::initializer_list<int>{(foo<Lower + Ints>(), 0)...};
}

// This simply makes it easier for the caller to use.
// We take the lower and upper bounds only.
template<int Lower, int Upper>
void call_foo_with_range() {
    call_foo_with_range_helper<Lower>(std::make_integer_sequence<int, Upper - Lower + 1>());
}   

int main() {
    int const UPPER = 10, LOWER = 5;

    call_foo_with_range<LOWER, UPPER>();
}

2 Comments

Thank you for your answer. I marked @TerraPass ' answer as best since I had an easier time understanding it (and I assume the people reading my code will too), but both are good solutions.
@TudorManole, No problem. The whole initializer_list business is unfortunate and once fold expressions are available, it can be replaced by (foo<Lower + Ints>(), ...);. Apart from that, std::integer_sequence is one of those things you tend to need a few times before you really appreciate it. In this case, the bulk of the work is supporting any range and not one starting at 0.
0

As far as I know templates are resolved to actual structures during compile time, so you you will have to pass int as a argument to function.

4 Comments

The thing is that the OP knows the integers at compile time. It's a matter of having the equivalent of a compile-time loop that can use them as template arguments.
I know, but I didn't know something like this existed. @NathanOliver pointed at something I wasn't aware of, std::integer_sequence
@MaciekGrynda If I were able to pass the integers as arguments to the function, it would indeed make my life much easier. But I can't, for reasons which are beyond the scope of the question.
@chris "It's a matter of having the equivalent of a compile-time loop". +1.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.