atzol
atzol

Reputation: 123

Calling Template Function with Multiple Parameter Values

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.

Upvotes: 0

Views: 1860

Answers (3)

MaciekGrynda
MaciekGrynda

Reputation: 703

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.

Upvotes: 0

TerraPass
TerraPass

Reputation: 1602

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>();

Upvotes: 3

Qaz
Qaz

Reputation: 61920

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>();
}

Upvotes: 3

Related Questions