0

The following code fails in compilation by g++ -std=c++11 compiler.

    # include<iostream>
    # include<vector>

    using namespace std;


    # define stlf(x)        x.begin(), x.end()
    # define repf(it, a, b) for(auto it = a ; it != b ; ++it)


    /*
    // Also, following alternative fails

    # define repf(it, a, b) for(auto it = a ; it != b ; ++it)
    # define stlf(x)        x.begin(), x.end()

    */



    typedef vector<int > vi;

    # define pd(x)  printf("%d", x);

    int main(void){

        vi arr(10, -1);

        repf(arr, stlf(arr))
            pd(arr[i]);


        return 0;
    }

1. Why is this happening ?

2. What could have been implementation problem for C++ Pre-Processor implementors, that they avoided this feature ?

3. How can I then use such shortcuts ??

Prem KTiw
  • 535
  • 1
  • 4
  • 17
  • 1
    You would do well to avoid such "shortcuts", it will just make it difficult for someone to read your code – M.M May 31 '17 at 05:55
  • 1
    Possible duplicate of [Why won't this compile and how can it be implemented so that it does?](https://stackoverflow.com/questions/2687284/why-wont-this-compile-and-how-can-it-be-implemented-so-that-it-does) – JaMiT Jul 03 '19 at 23:16

4 Answers4

5

Your two alternatives are identical. The order that macros are defined in is irrelevant to their expansion; it's only relevant what they are defined as at the time of expansion.

Why is this happening ?

You're invoking the macro repf with two arguments, but it takes three arguments. That is an error, plain and simple, so preprocessing fails.

What could have been implementation problem for C++ Pre-Processor implementors, that they avoided this feature ?

I think you're making an unwarranted assumption here. The issue isn't that the preprocessor "lacks" some "feature"; it's that your expectations of how the preprocessor works is wrong.

Presumably, you expect the preprocessor to do something like this:

  1. repf(arr, stlf(arr))
  2. repf(arr, arr.begin(), arr.end())
  3. for(auto it = arr.begin() ; it != arr.end() ; ++it)

...where from step 1 to step 2, stlf(arr) gets expanded; then its expansion is put into the call to repf, which then gets expanded at step 3.

The problem is, that is not how the preprocessor works. Given the example is broken, I can't illustrate the steps properly with this, so let's suppose we do this instead for illustration purposes:

#define FOO(X, Y) BAR(X, Y)
#define BAR(X,Y,Z) x is X y is Y z is Z
#define ACOMMAB A, B
FOO(ACOMMAB, C)

That last line expands to x is A y is B c is Z, and it works more like this:

  1. FOO(ACOMMAB, C)
  2. BAR(ACOMMAB, C)
  3. BAR(A, B, C)
  4. x is A y is B c is Z

Note that the inner macro doesn't expand first; rather, the outer macro does. Note also that this example injects a comma; so injecting a comma is definitely something you can in fact do, which I assume to be the "feature" you refer to that was avoided.

How can I then use such shortcuts ??

Given the preprocessor doesn't work the way you think it does, you probably don't want to use it to do what you think you want to use it to do... even for speed coding. stlf will gladly build two arguments for you for function calls, but macros are not functions. Ranged for seems to be your best bet.

H Walters
  • 2,634
  • 1
  • 11
  • 13
  • Nice Explanation !! So, is it not that if the implementation order would have been Inner to Outer, this feature could have been possible? And any way to verify the Outward to Inward expansion ? – Prem KTiw May 31 '17 at 08:29
  • 1
    @premktiw "any way to verify the Outward to Inward expansion" Don't take the order as described here literally; this "order" is just a device to describe the behavior. The key thing here is that the expansion of the argument is applied to the replacement list, not the call, and that's demonstrated by the fact that the example I gave works but your original doesn't (in g++ you can call the preprocessor using the `-E` flag, and the `-P` flag would clean up such output). This behavior is specified by the standard. – H Walters May 31 '17 at 15:24
  • ...the "order" device also helps visualize exceptions. For example, whether to even expand the argument depends on the replacement list; stringified arguments or arguments being pasted aren't expanded. Implementations may also forgo expanding an argument that doesn't appear in a replacement list. However, strictly speaking, expanding an argument may occur before putting its expansion in the replacement list. – H Walters May 31 '17 at 15:32
  • 1
    Apologies for comment spam, but thinking it over, there's a relevant sense in which this order _literally_ applies: The primary thing being done first is expanding the outer macro. That expansion occurs in a series of steps. The inner macro expansion occurs, if it occurs, during one of these steps. – H Walters May 31 '17 at 16:18
2

The issue is, as H Walters points out, the order in which macros do things

  • read (parse) arguments from the input token stream
  • expand macros in those arguments (unless they are involved with a # or ## operator)
  • substitute those (expanded) arguments into the body of the macro
  • rescan the body for other macros to expand.

Note that there are two places in the above where other macros may be expanded, so while the order may be wrong for what you want to do, you may be able to make it right by adding additional macro calls to get things to happen when you want. For example, if you have a macro

#define expand(...)   __VA_ARGS__

which does nothing (just takes one or more arguments and expands macros in them), you can use it to get additional macro expansion in the right place:

expand( repf expand((arr, stlf(arr))) )

will expand to your desired for(auto it = arr.begin() ; it != arr.end() ; ++it)

That may seem unwieldy, but you can wrap the calls to expand in another recrusive macro:

# define stlf(x)        x.begin(), x.end()
# define expand(...) __VA_ARGS__
# define repf(...) expand(repf2 expand((__VA_ARGS__)))
# define repf2(it, a, b) for(auto it = a ; it != b ; ++it)

and now repf(arr, stlf(arr)) expands the way you want it to.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
1

repf is expecting 3 arguments and you are passing just 2. You cannot use another macro's expansion result (stlf) as an argument (for another Macro). Pre-processor/compiler aren't designed this way.

I'm not against macro (they are extremely useful in many cases), but for this simple case you must not be using macro. It makes your program hard to read, maintain and debug. Avoid!

Ideally, range based for loop you should be using instead (no macro):

for(int a : arr) ...
Prem KTiw
  • 535
  • 1
  • 4
  • 17
Ajay
  • 18,086
  • 12
  • 59
  • 105
  • Well I can use expansion of one macro as an argument !! My main question is Question number 2. Also in competitive programming these shortcuts save a lot of time :) So I really needed it to work. e.g. of macro expansion as argument : sort(stlf(arr)); – Prem KTiw May 31 '17 at 07:06
0

It should be something like this:

define repf(it, a) for(auto it = std::begin(a) ; it != std::end(a) ; ++it)

But why are you using macros for this?

nakiya
  • 14,063
  • 21
  • 79
  • 118
  • It's not like that, so as to make use of repf(it, a, b) also for general loops like : int i; repf(i, 0, 10) :- will iterate 10 times for me !! :) – Prem KTiw May 31 '17 at 07:10