3

I'm trying to make a simple preprocessor loop. (I realize this is a horrible idea, but oh well.)

// Preprocessor.h

#ifndef PREPROCESSOR_LOOP_ITERATION

#define MAX_LOOP_ITERATION 16 // This can be changed.

#define PREPROCESSOR_LOOP_ITERATION 0

#endif

#if (PREPROCESSOR_LOOP_ITERATION < MAX_LOOP_ITERATION)
#define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1) // Increment PREPROCESSOR_LOOP_ITERATION.
#include "Preprocessor.h"
#endif

The issue is that it doesn't look like PREPROCESSOR_LOOP_ITERATION is being incremented, so it just keeps including itself infinitely. If I change the line to an actual integer (like 17), the preprocessor skips over the #include directive properly.

What am I doing incorrectly?

jarlh
  • 42,561
  • 8
  • 45
  • 63
Maxpm
  • 24,113
  • 33
  • 111
  • 170

3 Answers3

8

The "problem" is that macros are lazily evaluated. Consider your macro definition:

#define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1)

This defines a macro named PREPROCESSOR_LOOP_ITERATION and its replacement list is the sequence of five preprocessing tokens (, PREPROCESSOR_LOOP_ITERATION, +, 1, and ). The macro is not expanded in the replacement list when the macro is defined. Macro replacement only takes place when you invoke the macro. Consider a simpler example:

#define A X
#define B A

B // this expands to the token X

#undef A
#define A Y
B // this expands to the token Y

There is an additional rule that if the name of a macro being replaced is encountered in a replacement list, it is not treated as a macro and thus is not replaced (this effectively prohibits recursion during macro replacement). So, in your case, any time you invoke the PREPROCESSOR_LOOP_ITERATION macro, it gets replaced with

( PREPROCESSOR_LOOP_ITERATION + 1 )

then macro replacement stops and preprocessing continues with the next token.

You can perform limited arithmetic with the preprocessor by defining a sequence of macros and making use of the concatenation (##) operator, but it's quite tedious. You should consider using the Boost.Preprocessor library to help you with this. It will work with both C and C++ code. It allows for limited iteration, but what it does allow is extraordinarily useful. The closest feature that matches your use case is likely BOOST_PP_ITERATE. Other facilities like the sequence (BOOST_PP_SEQ) handlers are very helpful for writing generative code.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
5

EDIT: As James pointed out, my original solution did not work due to lazy evaluation of macros. If your compiler supports it, the macro __COUNTER__ increments by one every time it is called, and you can use it to do a simple preprocessor loop like this:

// Preprocessor.h
#define MAX_LOOP_ITERATION 16 // Be careful of off-by-one

// do stuff

#if (__COUNTER__ < MAX_LOOP_ITERATION)
#include "Preprocessor.h"
#endif

I verified this in Visual C by running cl /P Preprocessor.h.

irritate
  • 7,350
  • 1
  • 17
  • 11
  • Macro evaluation is lazy, so in the `#if` directive here, `PREPROCESSOR_LOOP_ITERATION` is replaced by `TEMPVALUE`, which is not defined as a macro, so macro replacement stops. – James McNellis Feb 17 '11 at 06:35
  • It was as you said. Updated my answer to something that worked, though probably compiler dependent. – irritate Feb 17 '11 at 08:11
  • 1
    As a side note, some other uses for the `__COUNTER__` macro appear in another question here: http://stackoverflow.com/questions/652815/has-anyone-ever-had-a-use-for-the-counter-pre-processor-macro – irritate Feb 17 '11 at 08:22
-1

Seriously, find another way to do this.

The preprocessor should be relegated to include guards and simple conditional compilations.

Everything else it was ever useful for has a better way to do it in C++ (inlining, templates and so forth).

The fact that you state I realize this is a horrible idea ... should be a dead giveaway that you should rethink what you're doing :-)

What I would suggest is that you step back and tell us the real problem that you're trying to solve. I suspect that implementing recursive macros isn't the problem, it's a means to solve a problem you're having. Knowing the root problem will open up all sorts of other wondrous possibilities.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 2
    +1. If you really need this kind of abuse at compile time, using a more powerful preprocessor such as `m4`. – Adam Rosenfield Feb 17 '11 at 05:15
  • 4
    There really isn't a "real" problem. I wanted to make a preprocessor loop for the sake of making a preprocessor loop. – Maxpm Feb 17 '11 at 05:19
  • 4
    @Maxpm: you have _way_ too much free time on your hands. Get back to work, or go spend some time with the family, or go rock climbing or something :-) – paxdiablo Feb 17 '11 at 05:21
  • 2
    I understand the answer, but I disagree with it. Prior to variadic templates, preprocessor looping was the closest thing. (With respect to maintainability and extensibility, I mean.) – GManNickG Feb 17 '11 at 05:48
  • 3
    There are [all sorts](http://stackoverflow.com/questions/2576868/x/2577102#2577102) of [interesting things](http://stackoverflow.com/questions/4796015/x/4801848#4801848) that can only be accomplished using [macros](http://stackoverflow.com/questions/3818277/x/3825181#3825181) (ok, that last one is kind of a joke, but the other two are quite serious). The preprocessor absolutely sucks: its behavior is very convoluted in many cases and is quite limited, but if you want to generate code without using any external tools, it's often the best tool for the job. – James McNellis Feb 17 '11 at 06:41
  • Hard to tell whether you're being tongue in check there, @James - I can't actually believe you're putting forward that unreadable cruft as a _pro_ for using the pre-processor. The first can be done by just keeping them sorted by ID and eyeballing them (even for large sets) or using `#define CURR LAST+1` and adding to the end. The second can also be doe at runtime with simple upper and lower bounds checking or, assuming you _have_ a non-contiguous case (and the one given is pretty artificial), with a switch. – paxdiablo Feb 17 '11 at 06:54
  • I was only joking about the third one. I use a set of macros for creating all my enums; among other things they provide conversions to and from string representations. Another time I have found the preprocessor to be quite useful was when I implemented a safe integer arithmetic library: I made heavy use of SFINAE to help select overloads based on whether operands were signed or unsigned, and using macros to generate the function declarations for the 22 or so operators cut hundreds of lines of code from the header file. – James McNellis Feb 17 '11 at 07:08
  • Yet, the person asking this question is eager to find out if there is a way of doing it regardless the reason. Let's then follow the scientific way and try to either provide a possible solution, a hint of it or remain silent if none present. – Radek Strugalski Jan 29 '23 at 22:26
  • @Radek, my answer still stands even for someone wanting to know how to do it regardless of the reason. That's because it's a near certainty it will result in unreadable or non-portable code (such as use of `__COUNTER__` which is not part of the C++ standard). I would treat questions asking for how to write an accounting package directly in machine language, or how to write an OS in Object-Oriented COBOL, much the same (suggesting it not be done). In fact, the accepted answer also follows that tack, suggesting the use of Boost preprocessor add-on rather than relying on the normal one. – paxdiablo Jan 29 '23 at 22:44