Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does third 'for' mean in #define for for(int z=0;z<2;++z)for

I have found a piece of code in a C++ program, it seems that it loops two times every for() loop in this program, but why does it need this third for in such preprocessor definition?

#define for for(int z=0;z<2;++z)for

like image 992
aretas_pau Avatar asked Apr 08 '16 18:04

aretas_pau


2 Answers

It replaces for with for(int z=0;z<2;++z)for. Obviously, that would turn

for (int i = 0; i < N; ++i) {
    // ...
}

into

for (int z=0;z<2;++z) for (int i = 0; i < N; ++i) {
    // ...
}

Thus creating two nested loops. Without that extra for it would be

for (int z=0;z<2;++z) (int i = 0; i < N; ++i) {
    // ...
}

Which is obviously incorrect.

Note that even though it's “correct” in the form you gave in your question, it doesn't mean it's a “good practice”. This is an example of excessive macro abuse and must be avoided. Here is one of the numerous examples how it could go wrong:

for (int z = 0; z < 5; ++z) {
    for (int i = 0; i < 3; ++i) {
        std::cout << z << std::endl; // this will never print 2, 3, 4
    }
}

This will expand into

for (int z=0;z<2;++z) for (int z = 0; z < 5; ++z) {
    for (int z=0;z<2;++z) for (int i = 0; i < 3; ++i) {
        std::cout << z << std::endl; // this will never print 2, 3, 4
    }
}

Meaning that you now have four nested loops, and that the inner loop will print the “invisible” z instead of the z you have declared in the outer loop (which becomes the second-level loop in the expanded code).

Another reason: as pointed by @stefan, it's a very bad idea to use keywords or other well-known identifiers as macro names. Makes one think of the infamous #define true false. And, as @HolyBlackCat mentions, it's also undefined behavior, meaning that as far as the standard is concerned, anything could happen. Ranging from the code “seemingly working” to a full-blown World War III with Martians (who invaded Earth to cleanse it from ugly code).

like image 79
Sergei Tachenov Avatar answered Sep 30 '22 11:09

Sergei Tachenov


Firstly, that macro is the ugliest thing I've ever seen. I don't recommend ever doing something like this.

The top answer from Sergey Tachenov is really great, but it should also be mentioned that this macro really makes every for loop run twice. This means the doubly nested loops shown below will execute 400 times (not 200 times)! This might be unexpected.

for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        // I'll be executed 100 times without the macro
    }
}

becomes

for (int z=0;z<2;++z) for (int i = 0; i < 10; i++) {
    for (int z=0;z<2;++z) for (int j = 0; j < 10; j++) {
        // I'll be executed 400 times with the macro
    }
}

It only gets worse from here. Consider an infinte loop like

int some_condition = 0;
for(;;) {
    // insert logic here for calculating whether to break out
    if (some_condition) {
        some_condition = 0; // set back to false for use down the line
        break;
    }
}

turns into

int some_condition = 0;
for (int z=0;z<2;++z) for (;;) {
    // insert logic here for calculating whether to break out
    if (some_condition) {
        some_condition = 0; // set back to false for use down the line
        break; // gets me out of for(;;)...but the outer loop repeats
    }
}

which bumps you out of the inner infinite loop if the condition is met....only to go right back in it. Now you might be stuck in an infinite loop.

Unexpected behavior is a good reason to avoid something, and macro shenanigans like this is very dangerous, and could be a real b**ch to debug. Imagine if some include directive had this ported in several header files deep...

like image 24
Matt Messersmith Avatar answered Sep 30 '22 10:09

Matt Messersmith