How to run code inside a loop only once without external flag?
Asked Answered
P

6

14

I want to check a condition inside a loop and execute a block of code when it's first met. After that, the loop might repeat but the block should be ignored. Is there a pattern for that? Of course it's easy to declare a flag outside of the loop. But I I'm interested in an approach that completely lives inside the loop.

This example is not what I want. Is there a way to get rid of the definition outside of the loop?

bool flag = true;
for (;;) {
    if (someCondition() && flag) {
        // code that runs only once
        flag = false;
    }        
    // code that runs every time
}
Pulverulent answered 17/7, 2013 at 13:33 Comment(13)
Why not move that code out of the loop?Bullnose
I don't see the problem with using a flag truthfully. If I didn't want to do that, I'd just move it elsewhere.Foreplay
Assuming the "move out of the loop" isn't what you are looking for, how do you know what should be executed only once, and "when"?Floater
@Bullnose The loop is an application loop, so it runs all the time. Making use of asynchronous threads would be too much for this.Pulverulent
@MatsPetersson I want to execute given blocks of code only once, when the block is reached the first time. This may be the first iteration of the loop but due to other conditionals it may be later, too.Pulverulent
Sounds like a typical case of if (flag) ... (or if (!flag) ... . This is the idiomatic (typical) way to do do this.Floater
I've updated your code example based on that. My answer now would be "just use a flag".Rosio
@MatsPetersson I'm looking for a way to get rid of defining the flags manually. Either there is an inline solution (the are no inline classes in C++, are they?). Or I need an external manager for that.Pulverulent
How many of these things do you have, and if there are many, then perhaps you need to think about an overall structure change to the code?Floater
@MatsPetersson Maybe it helps to explain the application. It is a video game. Inside the main loop, many tasks like input handling, AI updates, rendering, and so on is done. But there are some cases where I want to react to a global state change. For example the give the player an award for the first time he reaches a specific level. while(1){ if(level > 7){ once([=]{ award(); }); } }Pulverulent
Thinking outside of the box, you could move the code you want to execute into functions, then use a function pointer inside the infinite loop. If the code enters the function, at the end NULL the function pointer, or set it to an empty function. However, now that you've clarified what you're doing I think you just need to redesign the code. Have a manager of global state changes that holds a list of objects of changes. When the change occurs, remove that one from the list.Unmistakable
@Merlin069 Thanks for sharing your thoughts. Moving the code blocks in functions would be even more overhead than defining flags, sadly. But maybe it could be done with inline lambdas cleaning themselves?Pulverulent
If you just loop once regardless of what, then why don't you just use following? ` do{ }while(false); `Roasting
L
14

It's fairly hacky, but as you said it's the application main loop, I assume it's in a called-once function, so the following should work:

struct RunOnce {
  template <typename T>
  RunOnce(T &&f) { f(); }
};

:::

while(true)
{
  :::

  static RunOnce a([]() { your_code });

  :::

  static RunOnce b([]() { more_once_only_code });

  :::
}
Litalitany answered 17/7, 2013 at 13:45 Comment(3)
This is kind of what I am looking for. Could we destroy the instance in the constructor after executing the lambda?Pulverulent
@Pulverulent No, it has static storage duration, so it will be destructed at program shutdown. The question is why you'd need to do that. Note that the lambda is not stored in the struct, just executed and forgotten (i.e. it gets destructed). The struct instance itself will probably just occupy 1-4 bytes depending on your compiler without doing anythin with them.Litalitany
Okay, it just wouldn't have felt so hacky.Pulverulent
I
12

For a less convoluted version of Mobius's answer:

while(true)
{
  // some code that executes every time
  for(static bool first = true;first;first=false)
  {
    // some code that executes only once
  }
  // some more code that executes every time.
}

You could also write this using ++ on a bool, but that's apparently deprecated .

Injector answered 6/5, 2014 at 15:59 Comment(0)
R
3

a possibly cleaner way to write this, albeit still with a variable, would be as follows

while(true){
   static uint64_t c;
   // some code that executes every time
   if(c++ == 0){
      // some code that executes only once
   }
   // some more code that executes every time.
 }

The static allows you to declare the variable inside the loop, which IMHO looks cleaner. If your code that executes every time makes some testable change, you could get rid of the variable and write it like this:

while(true){
   // some code that executes every time
   if(STATE_YOUR_LOOP_CHANGES == INITIAL_STATE){
      // some code that executes only once
   }
   // some more code that executes every time.
 }
Reorientation answered 17/7, 2013 at 13:48 Comment(10)
Wouldn't c constantly increment until it exceeds the bounds of a short, then loop around and become 0 sometime again?Beyond
Got the idea. The comment above is right, incrementing c inside the following code block is more clever. Moreover, a boolean flag would do the trick, too.Pulverulent
yes I included the counter variable because it is potentially more useful in a general case.Reorientation
@Reorientation I think it's not since the only once code block is not likely to be reached every iteration. So the counter variable wouldn't represent the loop count.Pulverulent
@Pulverulent I was thinking the if statement should be executed every iteration, so c should be incremented every iteration. Do I have something wrong?Reorientation
@Reorientation I just noticed a thing. Don't you have to initialize c with a value before?Pulverulent
Come on, how do you think function level static variables work so that they are initialized only once? The compiler will generate a hidden flag and check it every time. Exactly the same thing, just with the compiler support.Bullnose
If you're worried about wrapping around why not just use a 64 bit int? A quick test on my laptop suggests that it would take it about 1000 years just to count to 1.8e19 (64 bit int's max).Qnp
that's probably cleanerReorientation
@Pulverulent according to this, the compiler will always zero initialize variables.Reorientation
S
0
#include <iostream>
using namespace std;

int main()
{
    int fav_num = 0;

    while (true)
    {
    if ( fav_num == 0)
    {
        cout <<"This will only run if variable fav_number is = 0!"<<endl;
    }
    cout <<"Please give me your favorite number."<<endl;
    cout <<"Enter Here: ";
    cin >>fav_num;
    cout <<"Now the value of variable fav_num is equal to "<<fav_num<<" and not 0 so, the if statement above won't run again."<<endl;
    }
    
    return 0;
}

OUTPUT

This will only run if variable fav_number is = 0!

Please give me your favorite number.

Enter Here: 1

Now the value of variable fav_num is equal to 1 and not 0 so, the "if statement" above won't run again.
Sunbeam answered 24/12, 2022 at 8:36 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Kinlaw
M
-1

If you know you only want to run this loop once, why not use break as the last statement in the loop.

Maladapted answered 17/7, 2013 at 19:44 Comment(0)
A
-2
1    while(true)
2    {
3        if(someCondition())
4        {
5            // code that runs only once
6            // ...
7          // Should change the value so that this condition must return false from next execution.
8        }        
9    
10        // code that runs every time
11        // ...
12    }

If you expecting the code without any external flag then you need to change the value of condition in last statement of the condition. (7th line in code snippet)

Ayakoayala answered 18/7, 2013 at 9:31 Comment(4)
Please read the comments under the question. For example if the player level exceeds a given number, he should get an award once. I can't lower the player level just to not hand in the award twice. while(1){ if(level > 7){ once([=]{ award(); }); } }Pulverulent
Please read again the comments under the question because the first question is that without using the flag how to implement then only he is thinking about the next options.Ayakoayala
What does it matter weather the flag is a boolean variable right before the while loop or another flag inside the condition function?Pulverulent
Here I'm not mentioned about the flag because I don't want to create any new variable or flag (Re-usability concept). By using the condition value we can manipulate the same. Just verify my code.Ayakoayala

© 2022 - 2024 — McMap. All rights reserved.