2

Here is some C++ code I'm playing around with:

#include <iostream>
#include <vector>

#define IN ,
#define FOREACH(x,y) for(unsigned int i=0;i<y.size();i++) { x=y[i];
#define ENDFOREACH }

using namespace std;

int main()
{
    vector<int> ints;
    ints.push_back(3);
    ints.push_back(4);
    ints.push_back(5);
    ints.push_back(6);

    FOREACH(int item IN ints)
        cout << item;
    ENDFOREACH

    return 0;
}

However, I get an error:

macro "FOREACH" requires 2 arguments, but only 1 given

The code compiles if I change the IN to a comma. How can I get the IN to take the place of a comma?

Update: for those interested, here is the final version, which, if I do say so myself, is quite nice.

#include <iostream>
#include <vector>

#define in ,
#define as ,
#define FOREACH_(x,y,z) \
        y x; \
        if(z.size()) x = z[0]; \
        for(unsigned int i=0,item;i<z.size();i++,x=z[i])
#define foreach(x) FOREACH_(x)

using namespace std;

int main()
{
    vector<int> ints;
    ints.push_back(3);
    ints.push_back(4);
    ints.push_back(5);
    ints.push_back(6);

    foreach(item as int in ints)
    {
        cout << item << endl;
    }

    return 0;
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Nathan Osman
  • 71,149
  • 71
  • 256
  • 361
  • 2
    Have you considered Boost.Foreach? (http://www.boost.org/doc/libs/1_42_0/doc/html/foreach.html) – James McNellis Apr 21 '10 at 23:46
  • 1
    This doesn't work because C++ is not Python. – rlbond Apr 21 '10 at 23:52
  • possible duplicate of http://stackoverflow.com/questions/1262063/c-preprocessor-macro-expansion-to-another-preprocessor-directive – dmckee --- ex-moderator kitten Apr 21 '10 at 23:54
  • ...also http://stackoverflow.com/questions/1736654/macros-as-arguments-to-preprocessor-directives and possibly others from http://stackoverflow.com/search?q=c+c%2B%2B+preprocessor+macro+expand . Based on George Edison's comment below. – dmckee --- ex-moderator kitten Apr 21 '10 at 23:56
  • @rlbond: Python is not the only language to have a foreach structure written similarly to this. Additionally, Python doesn't use any END* keywords. – Ponkadoodle Apr 22 '10 at 00:09
  • @dmckee: I can't find a real duplicate for this question via the first page of your search and the two you linked are definitely different. – Georg Fritzsche Apr 22 '10 at 00:09
  • @gf: They are the same because in both cases the issue is "can I have the preprocessor perform macro expansion on the body of a preprocessor directive?" and the answer is "No.". And if you read the OP's comment below, you'll see that he is not interested in foreach behavior in c++ but in the preprocessor hackery. That said, Andrey's trick is so nice I won't push for a close at this point. Heck I'd even vote to reopen it. – dmckee --- ex-moderator kitten Apr 22 '10 at 00:23
  • @dmckee: I took the problem to be *"how to i get macro argument expansion to work right?"* (and yes, i am feeling ignored below ;). – Georg Fritzsche Apr 22 '10 at 00:38
  • @gf: Dude! You've been robbed, man! /stoned voice – dmckee --- ex-moderator kitten Apr 22 '10 at 00:45

6 Answers6

4

Others have already explained why it doesn't compile as is.

In order to make it work you have to give that IN a chance to turn into a comma. For that you can introduce an extra level of "indirection" in your macro definition

#define IN , 
#define FOREACH_(x,y) for(unsigned int i=0;i<y.size();i++) { x=y[i]; 
#define FOREACH(x) FOREACH_(x)
#define ENDFOREACH } 

In this case you'll have to use some substitute for comma (like your IN) and can no longer specify comma explicitly. I.e. now this

FOREACH(int item IN ints) 
    cout << item; 
ENDFOREACH 

compiles fine, while

FOREACH(int item, ints) 
    cout << item; 
ENDFOREACH 

does not.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

The compiler doesn't expand the IN macro before it reads the arguments to FOREACH. In fact, I think this is intentional (so that you can pass a comma to a macro).

Unfortunately, you'll have to use FOREACH(int item, ints).

You could also #define IN (make it nothing) and then use FOREACH(int item, IN ints), which is not quite as nice, but is acceptable.

That said, you may just want to use STL or Boost for foreach, unless you specifically want to create your own.

Zifre
  • 26,504
  • 11
  • 85
  • 105
1

Expansion for IN doesn't happen early enough in your example, but you can pass the expanded version to another macro:

#define FOREACH(x) DO_FOREACH(x)
#define DO_FOREACH(x,y) for( ... ) ...
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
1
#define IN ,
#define XFOREACH(x,y) for(unsigned int i=0;i<y.size();i++) { x=y[i];
#define FOREACH(x) XFOREACH(x)
#define ENDFOREACH }

As previous posters have noted, the preprocessor does not expand macros in the arglist before it splits it into argument. However, as long as the macro doesn't use # or ##, it expands macros in the args before substituting them into the macro body, so an extra indirection does the trick

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

Check out BOOST_FOREACH - it does what you want

http://www.boost.org/doc/libs/1_35_0/doc/html/foreach.html

George Godik
  • 1,716
  • 1
  • 14
  • 19
0

The preprocessor doesn't expand the IN to a comma until after it reads the arguments to FOREACH.

I'm pretty sure that the c++ preprocessor is one pass only, so you'll have to use:

FOREACH(int item, ints)
    cout << item;
ENDFOREACH
zmbush
  • 2,790
  • 1
  • 17
  • 35