2

How do you write a macro that can be used as follows:

FOR(n)
    FOR(j, n)
        //do stuff

and that expands to a valid nested C++ for loop, with:

  • FOR(n) will use a loop variable named i (default case) and with an invariant of less than (<) n,
  • FOR(j, n) will use a loop variable named as the first argument (j) and with an invariant of less than (<) n?

I saw this kind of macro used by a competitive programmer programming C++, but I have not been able to write such macros myself.

dfrib
  • 70,367
  • 12
  • 127
  • 192
codemonkey
  • 33
  • 3
  • did you try something ? Using somebody else macros that you do not understand is worse than using macros ;) – 463035818_is_not_an_ai Nov 17 '20 at 11:28
  • 1
    Welcome to Stackoverflow. Please take the [tour] and read [How to ask?](https://stackoverflow.com/help/how-to-ask). It will earn you your first badge – 463035818_is_not_an_ai Nov 17 '20 at 11:30
  • MACRO doesn't have "overload", but there is variadic MACRO, and trick to count arguments and dispatch to other MACRO. – Jarod42 Nov 17 '20 at 11:31
  • 2
    I have read that you are aware that this is a bad practice, but I still want to emphasize that if you are a beginner at a certain programming language you should avoid looking at anything competitive related, you just learn/see bad habits that are often hard to get rid of later. – t.niese Nov 17 '20 at 11:39
  • I suggest you to take a look at `std::for_each` if you want to learn a fancy way to write loops. That will be a much better exercise, and you'll learn something that is actually useful – 463035818_is_not_an_ai Nov 17 '20 at 11:50
  • 2
    @idclev463035818: *for range* seems more natural than `std::for_each` (that I don't remember to use often). – Jarod42 Nov 17 '20 at 11:53
  • @Jarod42 right, thats a much better suggestion. I don't use `std::for_each` that much often either, but its a good blue print for other algorithms, if one understands that then other algorithms are just a tiny bit more complicated. – 463035818_is_not_an_ai Nov 17 '20 at 12:10

1 Answers1

4

I'm aware it's bad practice to do things like this [...]

Let's start off by emphasizing this once more: yes, this is horrible practice.


Now, what looks like macro overloading is actually macro dispatch, as covered in detail in e.g. the following Q&A:

Using the library code from the accepted answer:

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

we can define various FOR_N overloads e.g. as follows:

// Dispatch macro.
#define FOR( ... ) VA_SELECT( FOR, __VA_ARGS__ )

// Dispatch for 1 or 2 macro arguments for dispatch function.
#define FOR_1(N) for(int i = 0; i < N; ++i)
#define FOR_2(I, N) for(int I = 0; I < N; ++I)

used as follows:

int main() {
    int n = 3;
    FOR(n)
        FOR(j, n)
            std::cout << i << " " << j << "\n";
    /* 0 0
       0 1
       0 2
       1 0
       1 1
       1 2
       2 0
       2 1
       2 2 */
}

Note that what may look like (Pascalesque) scope of macroed for loops, is only limited to a single line, as they expand to

for(int i = 0; i < n; ++i)
    for(int j = 0; j < n; ++j)
        std::cout << i << " " << j << "\n";

and not

for(int i = 0; i < n; ++i) {
    for(int j = 0; j < n; ++j) {
        std::cout << i << " " << j << "\n";
    }
}

To achieve the latter, you need to similarly explicitly add block scopes:

FOR(n) {
    FOR(j, n) {
        std::cout << i << " " << j << "\n";
    }
}
dfrib
  • 70,367
  • 12
  • 127
  • 192