Most elegant way to write a one-shot 'if'

C++If StatementC++17

C++ Problem Overview


Since C++ 17 one can write an if block that will get executed exactly once like this:

#include <iostream>
int main() {
    for (unsigned i = 0; i < 10; ++i) {

        if (static bool do_once = true; do_once) { // Enter only once
            std::cout << "hello one-shot" << std::endl;
            // Possibly much more code
            do_once = false;
        }

    }
}

I know I might be overthinking this, and there are other ways to solve this, but still - is it possible to write this somehow like this, so there is no need of the do_once = false at the end?

if (DO_ONCE) {
    // Do stuff
}

I'm thinking a helper function, do_once(), containing the static bool do_once, but what if I wanted to use that same function in different places? Might this be the time and place for a #define? I hope not.

C++ Solutions


Solution 1 - C++

Use std::exchange:

if (static bool do_once = true; std::exchange(do_once, false))

You can make it shorter reversing the truth value:

if (static bool do_once; !std::exchange(do_once, true))

But if you are using this a lot, don't be fancy and create a wrapper instead:

struct Once {
    bool b = true;
    explicit operator bool() { return std::exchange(b, false); }
};

And use it like:

if (static Once once; once)

The variable is not supposed to be referenced outside the condition, so the name does not buy us much. Taking inspiration from other languages like Python which give a special meaning to the _ identifier, we may write:

if (static Once _; _)

Further improvements: take advantage of the BSS section (@Deduplicator), avoid the memory write when we have already run (@ShadowRanger), and give a branch prediction hint if you are going to test many times (e.g. like in the question):

// GCC, Clang, icc only; use [[likely]] in C++20 instead
#define likely(x) __builtin_expect(!!(x), 1)

struct Once {
    bool b = false;
    explicit operator bool()
    {
        if (likely(b))
            return false;

        b = true;
        return true;
    }
};

Solution 2 - C++

Maybe not the most elegant solution and you don't see any actual if, but the standard library actually covers this case:, see std::call_once.

#include <mutex>

std::once_flag flag;

for (int i = 0; i < 10; ++i)
    std::call_once(flag, [](){ std::puts("once\n"); });

The advantage here is that this is thread safe.

Solution 3 - C++

C++ does have a builtin control flow primitive that consists of "(before-block; condition; after-block)" already:

for (static bool b = true; b; b = false)

Or hackier, but shorter:

for (static bool b; !b; b = !b)

However, I think any of the techniques presented here should be used with care, as they are not (yet?) very common.

Solution 4 - C++

In C++17 you can write

if (static int i; i == 0 && (i = 1)){

in order to avoid playing around with i in the loop body. i starts with 0 (guaranteed by the standard), and the expression after the ; sets i to 1 the first time it is evaluated.

Note that in C++11 you could achieve the same with a lambda function

if ([]{static int i; return i == 0 && (i = 1);}()){

which also carries a slight advantage in that i is not leaked into the loop body.

Solution 5 - C++

static bool once = [] {
  std::cout << "Hello one-shot\n";
  return false;
}();

This solution is thread safe (unlike many of the other suggestions).

Solution 6 - C++

You could wrap the one-time action in the constructor of a static object that you instantiate in place of the conditional.

Example:

#include <iostream>
#include <functional>

struct do_once {
    do_once(std::function<void(void)> fun) {
        fun();
    }
};

int main()
{
    for (int i = 0; i < 3; ++i) {
        static do_once action([](){ std::cout << "once\n"; });
        std::cout << "Hello World\n";
    }
}

Or you may indeed stick with a macro, that may look something like this:

#include <iostream>

#define DO_ONCE(exp) \
do { \
  static bool used_before = false; \
  if (used_before) break; \
  used_before = true; \
  { exp; } \
} while(0)  

int main()
{
    for (int i = 0; i < 3; ++i) {
        DO_ONCE(std::cout << "once\n");
        std::cout << "Hello World\n";
    }
}

Solution 7 - C++

Like @damon said, you can avoid using std::exchange by using a decrementing integer, but you have to remember that negative values resolve to true. The way to use this would be:

if (static int n_times = 3; n_times && n_times--)
{
    std::cout << "Hello world x3" << std::endl;
} 

Translating this to @Acorn's fancy wrapper would look like this:

struct n_times {
    int n;
    n_times(int number) {
        n = number;
    };
    explicit operator bool() {
        return n && n--;
    };
};

...

if(static n_times _(2); _)
{
    std::cout << "Hello world twice" << std::endl;
}

Solution 8 - C++

While using std::exchange as suggested by @Acorn is probably the most idiomatic way, an exchange operation is not necessarily cheap. Although of course static initialization is guaranteed to be thread-safe (unless you tell your compiler not to do it), so any considerations about performance are somewhat futile anyway in presence of the static keyword.

If you are concerned about micro-optimization (as people using C++ often are), you could as well scratch bool and use int instead, which will allow you to use post-decrement (or rather, increment, as unlike bool decrementing an int will not saturate to zero...):

if(static int do_once = 0; !do_once++)

It used to be that bool had increment/decrement operators, but they were deprecated long ago (C++11? not sure?) and are to be removed altogether in C++17. Nevertheless you can decrement an int just fine, and it will of course work as a Boolean condition.

Bonus: You can implement do_twice or do_thrice similarly...

Solution 9 - C++

Based on @Bathsheba's great answer for this - just made it even simpler.

In C++ 17, you can simply do:

if (static int i; !i++) {
  cout << "Execute once";
}

(In previous versions, just declare int i outside the block. Also works in C :) ).

In simple words: you declare i, which takes default value of zero (0). Zero is falsey, therefore we use exclamation mark (!) operator to negate it. We then take into account the increment property of the <ID>++ operator, which first gets processed (assigned, etc) and then incremented.

Therefore, in this block, i will be initialized and have the value 0 only once, when block gets executed, and then the value will increase. We simply use the ! operator to negate it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnadaView Question on Stackoverflow
Solution 1 - C++AcornView Answer on Stackoverflow
Solution 2 - C++lubgrView Answer on Stackoverflow
Solution 3 - C++Sebastian MachView Answer on Stackoverflow
Solution 4 - C++BathshebaView Answer on Stackoverflow
Solution 5 - C++WaxratView Answer on Stackoverflow
Solution 6 - C++moooeeeepView Answer on Stackoverflow
Solution 7 - C++OreganopView Answer on Stackoverflow
Solution 8 - C++DamonView Answer on Stackoverflow
Solution 9 - C++Nick LouloudakisView Answer on Stackoverflow