Wann darf man eigentlich…

written by Martin Häcker on

…die Syntax einer Sprache modifizieren?

In C natürlich. Das Problem ist klar: Makros können ja beliebige Ersetzungen im Text vornehmen und dann mit vielen Makros aus C Pascal zu machen geht auch - aber das will man ja nicht, denn sonst hat man das gleiche Problem wie die Bourne-Shell die irgendwann niemand mehr weiterentwickeln konnte.

Klar.

Andererseits nervt es wirklich unter Cocoa z.B. für jede Iteration über einen Array das hier zu schreiben:

NSArray *someStrings = [NSArray arrayWithObjects:@"first", @"second", @"third", @"fourth", nil];

NSString *eachString = nil;
NSEnumerator *enumerator = [someStrings objectEnumerator];

while (eachString = [enumerator nextObject])
{
    NSLog(@"eachString is %@", eachString);
}

Das heißt, für eine Schleife (zwei Zeilen) ha man noch mal mindestens zwei weitere Zeilen nur für das Setup.

Das nervt so sehr, das viele Leute das etwas kürzen:

NSArray *someStrings = [NSArray arrayWithObjects:@"first", @"second", @"third", @"fourth", nil];

id eachString, enumerator = [someStrings objectEnumerator];
while (eachString = [enumerator nextObject])
{
    NSLog(@"eachString is %@", eachString);
}

Schon besser, aber man verliert die Typchecks (falls man die haben will) und man muss trotzdem noch eine langweilige Zeile schreiben, die immer gleich ist und sich nicht wirklich kürzen lässt.

Jetzt kann man natürlich unter C schön ein Makro bauen, was das schöner macht - aber, siehe Bourne Shell...

Aber, mit ObjC 2.0 gibt es das for(String *each in someStrings) konstrukt, das schon deutlich kürzer (und auch schneller - aber aus anderen Gründen) ist.

Also, doch mal ein Makro. Nach reiflichem (2 Minuten) überlegen hab ich mich entschieden es erst einmal mit einer for Schleife zu probieren, da das bedeutet das der Scope der Variablen schön beschränkt ist.

Ziel wäre es also, das hier schreiben zu können:

NSArray *someStrings = [NSArray arrayWithObjects:@"first", @"second", @"third", @"fourth", nil];

FOREACH(eachString, someStrings)
{
    NSLog(@"eachString is %@", eachString);
}

Und das sähe dann so aus:

#define FOREACH(each, collection) \
    for(id __enumerator = [collection objectEnumerator], each = [__enumerator nextObject]; \
        each; \
        each = [__enumerator nextObject])

In der Praxis hab ich es auf Wunsch meiner Kollegen noch etwas modifiziert, damit man den Typ der Variablen noch einstellen kann (auch wenn er bisher noch nicht verwendet wird - immerhin kann man ihn aber als Startpunkt für Dokumentations-Lookup verwenden).

/// Use like this:
// FOREACH(NSString *, each, arrayOfStrings)
// {
//      if ([each hasPrefix:@"foo"])
//          [self doSomething];
// }
// This macro is in preparation of the ObjC 2.0 Fast enumeration protocol
#define FOREACH(typeOfEach, each, collection) \
    for(id __enumerator = [collection objectEnumerator], each = [__enumerator nextObject]; \
        each; \
        each = [__enumerator nextObject])

Bei Verwendung, bitte e-mail. :-)