This question is strictly related to the C or C++ language capabilities. I don't recommend the code below as a design pattern. I don't use it and I don't encourage it. But I'm just curious to improve my knowledge!
I have a define that contains a label and a goto condition.
#define BROP(num, sum) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
rng1: \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto rng1; \
}
And later in the code I use it like this:
for (i = 0; i < iterations; i++)
{
BROP(num, sum);
BROP(num, sum);
BROP(num, sum);
// ...
}
I end up in a situation where the loop gets unrolled and the label gets redefined.
Is there a smart construct I can use to make the compiler rename the label each time the define is "instantiated"?
I know of ALL the alternatives of avoiding this statement but still, I don't know the answer to the question.
You really should make it a do
...while
loop:
#define BROP(num, sum) do{ \
bool again = false; \
num = rand_lcg(generated); \
if (num % 2) { \
do { again = false; \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
again = true; \
} while(again); } while(0)
See this for the old outerdo{
...}while(0)
trick.
If you (wrongly) insist on having a label and are using the GCC compiler (or Clang/LLVM which should be compatible on that), you could use local labels extension (i.e. with __label__
...)
You might also generate the label from the __LINE__
number using concatenation in the preprocessor. Take inspiration from
#define STUPID_LOOP_BIS(Test,Lin) do { \
lab##Lin: if (Test) goto lab##Lin; } while(0)
#define STUPID_LOOP_AT(Test,Lin) STUPID_LOOP_BIS(Test,Lin)
#define STUPID_LOOP(Test) STUPID_LOOP_AT(Test,__LINE__)
for obscure reasons you need all the three macros!
And use
STUPID_LOOP(x++ < 100);
STUPID_LOOP(y-- > 0);
on separate lines. Of course adapt and improve for your need.
You definitely should use and trust more the compiler optimization abilities and have static inline
functions. Not every test is compiled to a machine branch (e.g. because of CMOV instructions); not every loop is compiled to a machine loop (e.g. because of loop unrolling). You are probably losing your developer's time, and more importantly, you are disabling optimizations by your tricks (so your code will likely go slower, not faster).
If using GCC or Clang enable optimizations and warnings: so compile with
gcc -Wall -Wextra -O3 -mtune=native
Ignoring the why of it, the following version of BROP
compiles cleanly as both C and C++
#define BROP(num, sum, lbl) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
lbl : \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto lbl; \
}
I invoked it as
for (i = 0; i < 1000; i++)
{
BROP(num,sum, lbl1);
BROP(num,sum, lbl2);
}
This doesn't rely on any compiler extensions, so you should be able to use it across a large range of compilers.