Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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;
    }
};

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.


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.


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.


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

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


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";
    }
}