2

Adam Ko has provided a magnificent solution to this question, thanks Adam Ko.

BTW if, like me, you love the c preprocessor (the thing that handles #defines), you may not be aware there is a handy thing in XCode: right click on the body of one of your open source files, go down near the bottom .. "Preprocess". It actually runs the preprocessor, showing you the overall "real deal" of what is going to be compiled. It's great!


This question is a matter of style and code clarity. Consider it similar to questions about subtle naming issues, or the best choice (more readable, more maintainable) among available idioms.

As a matter of course, one uses loops like this:

for(NSUInteger _i=0; _i<20; ++_i)
  {
  .. do this 20 times ..
  }

To be clear, the effect is to to do something N times. (You are not using the index in the body.)

I want to signal clearly for the reader that this is a count-based loop -- ie, the index is irrelevant and algorithmically we are doing something N times.

Hence I want a clean way to do a body N times, with no imperial entanglements or romantic commitments. You could make a macro like this:

 #define forCount(N) for(NSUinteger __neverused=0; __neverused<N; ++__neverused)

and that works. Hence,

forCount(20)
  {
  .. do this 20 times ..
  }

However, conceivably the "hidden" variable used there could cause trouble if it collided with something in the future. (Perhaps if you nested the control structure in question, among other problems.)

To be clear efficiency, etc., is not the issue here. There are already a few different control structures (while, do, etc etc) that are actually of course exactly the same thing, but which exist only as a matter of style and to indicate clearly to the reader the intended algorithmic meaning of the code passage in question. "forCount" is another such needed control structure, because "index-irrelevant" count loops are completely basic in any algorithmic programming.

Does anyone know the really, really, REALLY cool solution to this? The #define mentioned is just not satisfying, and you've thrown in a variable name that inevitably someone will step on.

Thanks!


Later...

A couple of people have asked essentially "But why do it?"

Look at the following two code examples:

for ( spaceship = 3; spaceship < 8; ++spaceship )
   {
   beginWarpEffectForShip( spaceship )
   }

forCount( 25 )
   {
   addARandomComet
   }

Of course the effect is utterly and dramatically different for the reader.

After all, there are alresdy numerous (totally identical) control structures in c, where the only difference is style: that is to say, conveying content to the reader.

We all use "non-index-relative" loops ("do something 5 times") every time we touch a keyboard, it's as natural as pie.

So, the #define is an OKish solution, is there a better way to do it? Cheers

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Could you give me a few examples of when you just need to do something N times, without using the index inside the loop for anything? – Matti Virkkunen Jan 30 '11 at 16:41
  • The `#define` will *still* create that index variable that you never use. All the compiler really does is substitute your `forCount()` macro for the actual `for()` construct. That's why the nesting problem you mentioned exists. – BoltClock Jan 30 '11 at 16:41
  • What problem are you trying to solve here? Why is a normal for loop unsuitable? No one is forcing you to use the index variable if you don't want to... – Asher Dunn Jan 30 '11 at 16:45

7 Answers7

6

You could use blocks for that. For instance,

void forCount(NSUInteger count, void(^block)()) {
    for (NSUInteger i = 0; i < count; i++) block();
}

and it could be used like:

forCount(5, ^{
    // Do something in the outer loop
    forCount(10, ^{
        // Do something in the inner loop
    });
});

Be warned that if you need to write to variables declared outside the blocks you need to specify the __block storage qualifier.

  • If there’s a variable declared outside the block and you need to write to it, `__block someType someVariable;` instead of `someType someVariable;`, e.g. `__block float average;` outside the block. –  Jan 30 '11 at 17:10
4

A better way is to do this to allow nested forCount structure -

#define $_TOKENPASTE(x,y)   x##y
#define $$TOKENPASTE(x,y)   $_TOKENPASTE(x, y)

#define $itr          $$TOKENPASTE($_itr_,__LINE__)
#define forCount(N)   for (NSUInteger $itr=0; $itr<N; ++$itr)

Then you can use it like this

forCount(5)
{
    forCount(10)
    {
        printf("Hello, World!\n");
    }
}

Edit: The problem you suggested in your comment can be fixed easily. Simply change the above macro to become

#define $_TOKENPASTE(x,y)   x##y
#define $$TOKENPASTE(x,y)   $_TOKENPASTE(x, y)

#define UVAR(var)           $$TOKENPASTE(var,__LINE__)
#define forCount(N)         for (NSUInteger UVAR($itr)=0, UVAR($max)=(NSUInteger)(N); \
                                 UVAR($itr)<UVAR($max); ++UVAR($itr))

What it does is that it reads the value of the expression you give in the parameter of forCount, and use the value to iterate, that way you avoid multiple evaluations.

koo
  • 2,888
  • 1
  • 23
  • 29
  • **Magnificent -- the actual solution to the question. Great work, Adam. 50 people looked at the question and only you knew the answer!** – Fattie Jan 31 '11 at 07:12
  • What intrigued me the most is that even you have line number collisions, such as `forCount(5) forCount(10) printf("Hello\n");` in one line, (so you can see variables with the same names appear in the debugger) it will still print the exact number of lines as expected. I don't really know what is going on in the c99 preprocessor, so I can't really give a reason for that. – koo Feb 02 '11 at 22:40
  • The predefined macro `__LINE__` always gives you the line number in your current file. There are many others like `__FILE__` or `__TIME__` but they can't be used in variable names because they give you string literals. – koo Feb 02 '11 at 22:51
4

On possibility would be to use dispatch_apply():

dispatch_apply(25, myQueue, ^(size_t iterationNumber) {
     ... do stuff ...
});

Note that this supports both concurrent and synchronous execution, depending on whether myQueue is one of the concurrent queues or a serial queue of your own creation.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • You'd need to use `__block` if you have a variable declared outside the block that you want to write to inside the block, sure. A common mistake is applying `__block` to, say, an `NSMutableArray*` declared outside; adding objects to an array doesn't require `__block`. – bbum Feb 03 '11 at 06:53
  • Note that `enumerateWithBlock:` on `NSArray` also has an iterations-number argument. And a flag to stop iteration. – bbum Feb 03 '11 at 06:55
2

To be honest, I think you're over addressing a non-issue.

If want to iterate over an entire collection use the Objective-C 2 style iterators, if you only want to iterate a finite number of times just use a standard for loop - the memory space you loose from an otherwise un-used integer is meaningless.

Wrapping such standard approaches up just feels un-necessary and counter-intuitive.

John Parker
  • 54,048
  • 11
  • 129
  • 129
1

No, there is no cooler solution (not with Apple's GCC version anyways). The level at which C works requires you to explicitly have counters for every task that require counting, and the language defines no way to create new control structures.

Other compilers/versions of GCC have a __COUNTER__ macro that I suppose could somehow be used with preprocessor pasting to create unique identifiers, but I couldn't figure a way to use it to declare identifiers in a useful way.

What's so unclean about declaring a variable in the for and never using it in its body anyways?

zneak
  • 134,922
  • 42
  • 253
  • 328
  • 1
    +1 Exactly, it's not like the compiler makes any noise about the counter variable being "unused" anyway. – BoltClock Jan 30 '11 at 16:39
0

FYI You could combine the below code with a define, or write something for the reader to the effect of:

//Assign an integer variable to 0. 
           int j = 0;
           do{
                //do something as many times as specified in the while part
            }while(++j < 20);
Michael
  • 1,786
  • 5
  • 23
  • 42
-1

Why not take the name of the variable in the macro? Something like this:

#define forCount(N, name) for(NSUInteger name; name < N; name++)

Then if you wanted to nest your control structures:

forCount(20, i) {
    // Do some work.
    forCount(100, j) {
        // Do more work.
    }
}
Jeff Kelley
  • 19,021
  • 6
  • 70
  • 80