Timo
Timo

Reputation: 881

Modern C++ way to repeat code for set number of times

Very simply, is there a simpler way to repeat a block for a certain number of times, where the block inside does not need the counter variable? The trivial solution is of course

for (int i = 0; i < repetitions; ++i) {
  //do your thing, i is not used here
}

However, now that we have ranged for, standard algorithms and other fancy constructs for iterating over containers, in comparison this is actually starting to feel like a lot of boilerplate and details for what should be an even simpler case. For example we're not interested in the variable i at all etc.

The closest thing to a concrete problem is this: when I encounter a for loop such as above, I need to scan through the code block to see if i is actually used, or if it's just a dummy counter. The declaration of a for loop which actually wants to do something with the integers 0 to repetitions - 1 will look identical. So a repeat (n) -type construct would have the extra semantic information that all the iterations will be the same, except for potential side-effects.

One option is to make a template

template<class functor>
repeat(functor fun, unsigned n) {
   for (unsigned i = 0; i < n; ++i)
     fun();
}

and call

repeat([&](){
  //do your thing
}, repetitions)

but this really seems like overengineered overkill for a simple problem. This could be macroized to make the usage a bit nicer, but that certainly won't help with the overengineered feel.

So one valid answer is that I'm on a wild goose chase here, and should just use the good old for loop with counter.

Any standard C++ is fine, including upcoming standards.

Related questions such as How to create a loop in C++ that loops a certain amount of times? and How to create a loop in C++ that loops a certain amount of times? are beginners asking for some way to achieve this, whereas I'm specifically asking for a modern, clean and elegant way to achieve this. c++ repeat N iterations is very close, though the difference here is that I'm asking for any alternatives, not necessarily included in std::.

Upvotes: 14

Views: 17524

Answers (3)

Chef Gladiator
Chef Gladiator

Reputation: 1028

"Hand-made" repeat is somewhat problematic. Should there be break or continue? One can throw an exception from "inside" repat, and "break out". But some projects do disallow exceptions.

Also in the presence of a standard for range loop one does not need to "scan through the code block to see if i is actually used, or if it's just a dummy counter.". That argument is done with.

To have compiler intrinsic repeat might be not such a bad idea, and certainly would be easy to add.

    // C++ 25 -- perhaps 
    repeat( 42 ) {
        std::printf("\nThis is 42 lines of text.");
        continue; // allowed
        break ;   // allowed
    }

REPEAT(N) macro is tempting, but macros are forbiden. If not compiler intrinsic, user defined repeat is not that "simple and elegant". Here is what I might be using:

// the repeat
template< typename CALLABLE_, typename ... Args >
void repeat( size_t N, CALLABLE_ fun_, Args ... args)
{
    for ( auto k = 0 ; k <  N ; k++ ) {
        fun_(args ...);
    }
}

Now to use the above one would most likely call it with lambda. Usage is as expected:

// reachable from inside `repeat`
int result {} ;

repeat( 0xF ,  
 [&] {  
     ::std::wprintf( L"\n random word:\t '%s'", random_word());
     result++ ;
    }
 );

The "modern way" is not always "the elegant vintage way".

Upvotes: 0

user1180790
user1180790

Reputation:

Introduction

Modern does not necessarily means using newest feautures.

Solution

One of the easiest solutions it to use simple for loop as below:

for (auto _ = times; _--;) [[likely]] statement;

…where:

Example

Example usage:

constexpr auto f(int Value) noexcept
{
    for (auto _ = 3; _--;) [[likely]] ++Value;
    return Value;
}

int main()
{
    constexpr auto i = f(2);
    return i;
}

Constant i variable is declared with initial value of 2, increased 3 times by one in f's for loop, taking final value of 5 and being used as program returned value.

Notes

  • Some compiler implementations may potentially take use of [[likely]] attribute in future.
    If times constant is likely to be zero, use [[unlikely]] attribute instead.
    Likelihood attribute is a feauture, skip in earlier versions.
  • _ name is commonly used as meaningless to name a discardable variable.

Upvotes: 6

r3mus n0x
r3mus n0x

Reputation: 6154

Instead of a modern C++ way, how about an old C way but without an index:

while (repetitions--)
    fun();

Of course you still need a variable for repetitions though.

Upvotes: 6

Related Questions