12

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::.

Timo
  • 739
  • 1
  • 6
  • 13
  • 9
    Possible duplicate of [c++ repeat N iterations](https://stackoverflow.com/questions/54924901/c-repeat-n-iterations) – cfillion Apr 05 '19 at 07:41
  • 3
    Well...a simple function abstracting the loop may be enough. But still I see no problem on having old trusty loops. – Netwave Apr 05 '19 at 07:41
  • 6
    Is there any problem with a normal loop? C++ even allows declaring counter inside of it. – user7860670 Apr 05 '19 at 07:42
  • 1
    @VTT no, no actual problem of course. This is more about me nerdsniping myself about trying to write things in the simplest possible way, which expresses intent most directly. – Timo Apr 05 '19 at 07:48
  • @cfillion you're right, I somehow missed that! I'm tempted to delete my question now, however, there does seem to be commentary, an answer and a few upvotes already... – Timo Apr 05 '19 at 07:49
  • 2
    @Timo *"write things in the simplest possible way, which expresses intent most directly"* - then you should certainly try using COBOL: `Perform something 5 times.` – user7860670 Apr 05 '19 at 07:52
  • Embarrassing comment... Back to the good old macro: `#define TIMES(N) int i = 0 ; i < N; ++i` and use `for(TIMES(10)) std::cout << i << "\n";` or even: `#define REPEAT(N) for(size_t i = 0 ; i < N; ++i)` and use `REPEAT(10) std::cout << i << "\n";` – user1810087 Apr 05 '19 at 08:05
  • You have to first come up with a convincing reason why a for loop is problematic, without such you are fixing what is not broken. You might as well ask for having a "modern way" to add two ints. – Passer By Apr 05 '19 at 08:33
  • @VTT I'll start looking for a COBOL compiler for the STM32 -series... :D More seriosuly, probably the closest thing to a concrete problem with the standard for loop used for this purpose is this: when I see `for (int i = 0; i < repetitions; ++i) {...}`, I need to scan through the block of code to see if `i` is actually used in the loop, or if it's just a dummy counter. – Timo Apr 05 '19 at 08:35
  • @PasserBy see the edit – Timo Apr 05 '19 at 08:39
  • @Timo You can name it appropriately instead of using meaningless name such as `i`. I would write `for(auto remaining_iterations_count{iterations_count}; 0 < remaining_iterations_count; --remaining_iterations_count)` if that counter is not used in the loop or `for(decltype(iterations_count) iteration_index{}; iteration_index < iterations_count; ++iteration_index)` if that index is supposed to be used inside of loop. PS COBOL syntax is underrated. – user7860670 Apr 05 '19 at 08:40

3 Answers3

5

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.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • 6
    `repetitions` *is* an index – user7860670 Apr 05 '19 at 07:46
  • 7
    related: [What is the --> operator in `while (repetitions --> 0)`](https://stackoverflow.com/q/1642028/5470596) :D – YSC Apr 05 '19 at 07:51
  • In my opinion, defining a counter is completely inconsistent with human thinking.I gave my suggestion related to https://stackoverflow.com/questions/55654319/why-c-has-no-concise-syntax-that-allow-executing-an-operation-multiple-times-w. – Crawl.W Sep 23 '20 at 07:35
5

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.
0

"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".

Chef Gladiator
  • 902
  • 11
  • 23