6

wikipedia (here) gives a compile time unrolling of for loop....... i was wondering can we use a similar for loop with template statements inside... for example...

is the following loop valid

template<int max_subdomain>
void Device<max_sudomain>::createSubDomains()
{
    for(int i=0; i< max_subdomain; ++i)
    {
        SubDomain<i> tmp(member);
        ...
        // some operations on tmp
        ...
    }
}

SubDomain is a class which takes in the a template parameter int and here has been constructed with an argument that is a member of the Device class.

Thanks for the answer guys... now that you know what i want... is there anyway i achieve what i want to??

i finally got what i wanted.............. instead of using the for loop directly... one can instead use the Boost::MPL for_each construct. I haven't yet implemented it but I am guessing that this provides a way to do what i wanted.....

I took the answer from another stack overflow question here... However the comment to the same question decries its use because it would be very slow (for large for loops of course)... however.. for loops which are not large i don't think there should be any bloating... i'll try the code and let you know the results....

the use is illustrated well in the example

Community
  • 1
  • 1
  • Have you tried it? What do you mean by valid? What is the question? – Luc Danton Jul 19 '11 at 04:50
  • well actually... the things are quiet messy in my code right now... –  Jul 19 '11 at 05:17
  • @Jayesh, please refer to the edited answer. I had made a mistake in the demo code. – iammilind Jul 19 '11 at 05:59
  • The "decries its use" comment was specific to a 2D loop which didn't actually need compile-time unrolling. If you need `SubDomain` for all values of ``, then naturally the compiler has to instantiate all of them. That's unavoidable work. – MSalters Jul 19 '11 at 09:06

3 Answers3

8

There's a stadard solution for this. Convert iteration into recursion.

template<int i>
void Device::createSubDomains()
{
    SubDomain<i> tmp(member);
    // some operations on tmp
    createSubDomains<i-1>();
}
template<>
void Device<-1>::createSubDomains()
{
  // End of recursion.
}

Note: you can't use a runtime if(i!=0) createSubDomains<i-1>();.

2017 Note: you can now use a compile-time if constexpr(i!=0) createSubDomains<i-1>();

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • yaa.. i knew of it... it works too..... but when i saw the for loop example in wiki just had to ask........ thanks for the answer anyway... if i'll use that or not depends on some other implementations in the class... i'll have to try it out and see... will let you know which one i choose and why... –  Jul 19 '11 at 16:46
2

Even though you already accepted @iammilind 's answer, let me propose another one, because his reasoning for why i is not compile-time-constant is was not correct.

Suppose you have

    template<unsigned int MAX> struct SubDomain {...};
    ...

and you want to declare an instance of it ...

    SubDomain<i> tmp(member);

then i must be a commonly so-called compile-time-constant. What is that?


Pedantry

The standard assigns the term nontype template argument to template arguments that are not types (D'Oh).

14.3.2 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— ... [more follow, but are not relevant]

Right the first point contains a reference for further research: an integral constant-expression. This leads us to

5.19 Constant expressions [expr.const]

In several places, C + + requires expressions that evaluate to an integral or enumeration constant: as array bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2), as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).

Then, the key is:

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions.


Pedantry application

If we look back at your loop:

for (int i=...
    ...
    SubDomain<i>

then we can now observe that i is not allowed there. Why? Because i is NOT a const variable.

The observing reader might now think you can circumvent this:

for (int i=...
        ...
        const int I = i;
        SubDomain<I>

But is this really allowed? Negative, I = i is not an integral constant-expression, because i is not. It helps to realise that the rule for integral constant-expressions is applied recursively.

E.g., the following is valid code:

template <int> void foo() {}     
int main () {
    const int ccI = 0;
    const int ccJ = ccI*2;
    const int ccK = ccJ/4;     
    foo<ccK>();
}

But if make just one part of the chain non-const, then ccK is not considered integral constant anymore:

template <int> void foo() {}     
int main () {
          int ccI = 0;
    const int ccJ = ccI*2;  // not compile time constant
    const int ccK = ccJ/4;  // same

    foo<ccK>();             // error
}


Summary

So, in human readable form, template arguments that are not types, but (integer) values, must be compile-time-constant:

  • the initializer of a compile-time-constant must only involve other compile-time-constants
  • a literal value is a compile-time-constant
  • an enum value is a compile-time-constant
  • function-calls don't give compile-time-constants (for some advanced reasons)
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
0

Re-Edit:

My previous answer was correct. I have tried your code, it's giving compiler error. You cannot declare objects like that, as i cannot remain a compile time constant (as you are intending to do i++). template parameter must always be compile time constants. Here is the demo.

Also note that, loop unrolling is done for normal loops also, as part of optimization by compilers. It's not limited to templates.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    you probably got it right the first time... Your demo is not the same as the example @Jayesh gives. In your code, you instantiate `A` with `N`, which is indeed a compile time constant. In the OP's example, he tries to instantiate the template using `i`, which is a regular variable. That can't work, because the compiler can't instantiate classes of type unknown at compile time. So the OP's code is not valid, and will not compile. – Eran Jul 19 '11 at 05:27
  • 1
    @eran, thanks for pointing out the mistake. I have re-modified the answer back. – iammilind Jul 19 '11 at 06:00
  • thanks.... i didn't actually try using it right now but now i did.. and it gave me an error too...... –  Jul 19 '11 at 06:14
  • please refer to the last line of my edited question.... is there anyway to achieve what i want without using dynamic polymorphism? everything is known at the compile time... but it is not known while writing the code.... –  Jul 19 '11 at 06:20
  • @Jayesh, unfortunately there is way. Because `max_subdomain` can be any constant and we cannot declare the object based on that. But if you can describe **what was your exact goal to declare such objects** in a new question then you may get a good help to find a better way. – iammilind Jul 19 '11 at 06:30
  • ohk...... i'll write a new question...... i'm pretty bad at communication stuff.... lets hope i do it right this time........ –  Jul 19 '11 at 06:34
  • i think i have found the answer to the question... in a different way though..... using the for_each construct from Boost MPL –  Jul 19 '11 at 08:08
  • -1: `as soon as you do i++` `i cannot remain a compile time constant` -> `i` never was a compile time constant. It was declared as a mutable variable, and therefore it cannot _remain_ a compile time constant, because it never _was_ – Sebastian Mach Jul 19 '11 at 09:17
  • @phresnel, you are mistaken. Declaring `i` as `const int` indeed makes it compile time constant. see here: http://www.ideone.com/Tjgsn. But then you cannot do `i++`. I wrote this intentionally on the philosophy of the question. – iammilind Jul 19 '11 at 09:21
  • @iammilind: Reading your answer as is tells me that `++` eliminates the compile-time-ness of some variable. But it does not. The declaration may do that. And by that, no, I am not mistaken, but thanks for trying to teach me. And btw, just declaring something as `const` does not necessarily make it compile time constant. See [this demo](http://www.ideone.com/xBOp6). – Sebastian Mach Jul 19 '11 at 09:30
  • @phresnel, your example is in different context. In `for` loop OP was initializing with literal `0`. If you misunderstood the answer, it's up to you. – iammilind Jul 19 '11 at 09:36
  • @iammilind: Why is it relevant whether `i` was declared in the intializer of a for-loop or not? If you look into the standard, you'll find that the intializer of a for-loop is either an `expression-statement` or a `simple-declaration`, therefore, `int i` in a for-loop initializer is equivalent to one that is not in a for-loop initializer. Try to do your homework first, please. – Sebastian Mach Jul 19 '11 at 09:41
  • @iammilind: If that was not what you intended to question, maybe this: The OP did declare the loop-counter exactly as `int i=0`. So did I in my linked example. Both the OP and ME did __not__ make it `const`, which is why it is __not__ a compile time constant. To make an integer compile-time-constant, you have to use `const` or in C++0x `constexpr`, __and__ you must initialize it with a literal value and/or another compiler-time-constant expression. See [this demo](http://www.ideone.com/4xUep), which is well formed C++. – Sebastian Mach Jul 19 '11 at 09:44
  • thanks guys...... @iammilind: thanks.... especially for all the examples you wrote for illustration.. thanks..... –  Jul 19 '11 at 16:53