Most elegant way to write a one-shot 'if'
C++If StatementC++17C++ 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.