Nicht nur das es jede menge kranke Mc Donalds Werbung gibt.
Noch viel kranker sind nur die Leute die bei McDonalds einkaufen...
Nicht nur das es jede menge kranke Mc Donalds Werbung gibt.
Noch viel kranker sind nur die Leute die bei McDonalds einkaufen...
Fehlt noch ein letzter Schritt - der erstaunlicherweise ganz einfach war und ausserdem noch ein ganz anderes Problem löste...
Zuerst die Implementierung:
#define LOG(what, variableArguments...) \ [[Log sharedLog] logObjcType:@encode(typeof(what)) \ arguments:(void *)what , ##variableArguments] - logObjcType:(char *)typeString arguments:(void *) what, ...; { id formatString = [self stringFromType:typeString inValue:what]; va_list variadicArguments; va_start(variadicArguments, what); id logString = [[[NSString alloc] initWithFormat:formatString arguments:variadicArguments] autorelease]; va_end(variadicArguments); return [self log:logString]; }
Eigentlich hatte ich hier mehr Probleme erwartet.
Ganz nebenbei löst das auch noch ein Problem von ganz zu Anfang:
LOG(@"%@", [NSString stringWithFormat:@"%s,%s", "fnord", "fnord"]);
Das macht jetzt keine Probleme mehr, da die zusätzlichen Komas vom Präprozessor (fälschlicherweise) als zusätzliche Argumente zur dem variadischen Makro aufgefasst werden. Hehe...
Jetzt fehlt nur noch ein Backend für den Logger und ich bin glücklich. Andererseits... da gibt es natürlich auch Log4Cocoa das ich einfach erweitern könnte.
Hm. Mal schauen.
Wendet man @encode()
jetzt auf ein paar Deklarationen an, erhält man folgende Ergebnisse:
#define LOG(what) [[Log sharedLog] logObjcType:@encode(typeof(what)) arguments:(void *)what] LOG(@"fnord"); // @ LOG("foo"); // [3c] char *string = "fnord"; LOG(string); // *
Ich parse das so:
#define RAISE_UNSUPPORTED_TYPE(encodedType) \ [NSException raise:NSInvalidArgumentException \ format:@" you are trying to log something that is not a string: %s", encodedType] - stringFromType:(char *)encodedType inArray:(void *)what; { if ('[' != *encodedType) RAISE_UNSUPPORTED_TYPE(encodedType); encodedType++; unsigned length = strtol(encodedType, &encodedType, 10); if ('c' != *encodedType) RAISE_UNSUPPORTED_TYPE(encodedType); return [NSString stringWithCString:what length:length-1]; // \0 ignored } - stringFromType:(char *)encodedType inValue:(void *)what; { if ('@' == *encodedType) return [(id)what description]; if ('*' == *encodedType) return [NSString stringWithCString:(char *)what]; if ('[' == *encodedType) return [self stringFromType:encodedType inArray:what]; RAISE_UNSUPPORTED_TYPE(encodedType); return nil; /* never reached */ }
(Vorschläge wie man das besser machen kann, bitte gerne an mich! ( z.B. via spamfaenger ät gmx de))
Fehlt nur noch die Erweiterung auf beliebig viele Argumente. Dazu aber morgen mehr. :)
Das Problem ist also ein Makro zu schreiben, das typsicher und nach Typen überladen ist, eine variable Anzahl von Argumenten erhält und zudem einfach und verständlich ist.
Zugegeben, mir ist noch nicht klar wie ich das umsetzen kann.
Aber, wenn man das Problem nur ein klein wenig vereinfacht, und nur versucht eines oder mehrere Argumente zu unterstĂĽtzen...
Also zum Beispiel ein LOG()
Macro, dass als erstes Argument irgend etwas erhalten kann (für den Anfang, irgend etwas das sich in einen String umwandeln lässt) dann erhält man etwas das sich so verwenden lässt:
LOG(@"fnord"); LOG("fnord"); char *string = "fnord"; LOG(string); LOG(@"%@", @"fnord"); LOG(@"%s", "fnord"); LOG(@"%d", 10); LOG("%s", "fnord"); LOG(@"%@", [NSString stringWithFormat:@"%s,%s", "fnord", "fnord"]);
Die Implementierung ist allerdings etwas tricky.
Typinvariante Makros wie MAX(x, y)
schreibt man ĂĽblicherweise so:
#define MAX(x, y) ({ \ typeof(x) __x = (x); \ typeof(y) __Y = (y); \ (__x < __y) ? __x : __y; \ })
Das bedeutet durch die speziellen klammern ({ ... })
wird das Makro wie eine Funktion behandelt (eine GCC-Extension) und die ĂĽbergebenen AusdrĂĽcke nur einmal auswertet und in einer Variablen vom korrekten Typ speichert. int m = MAX(i++, ++j);
ist damit also kein Problem. SchlieĂźlich ist der Wert der letzten Zeile der "RĂĽckgabewert" der Makro-Funktion
Loggen ist aber schon noch ein Problem, da mit diesem Trick, der Typ noch nicht herauszufinden ist. Mein erster Ansatz mit __builtin_types_compatible_p(typeof(aValue), char *) etc. brachten mich da nicht weiter. Es braucht noch die @encode() Anweisung von Obj-C. Direkt im Makro mit if (@encode(char *) == @encode(typeof(aValue)))
war es dann allerdings auch noch nicht obwohl ich gerne mit __builtin_choose_expr( aConstant, expressionOne, expressionTwo ) zur Compilezeit alles erledigt hätte.
Nun ja, dann eben zur Laufzeit mit @encode()
.
#define LOG(aValue) [[Log sharedLog] logObjcType:@encode(typeof(aValue)) value:aValue]
Die erzeugt aus einem Typ nämlich einen C-String - der zur Laufzeit geparst werden kann um daraus herauszulesen wie der void-Pointer auf aValue
interpretiert werden muss.
Wie ich das gemacht hab - morgen. :)
(Zu "Feuer Frei" von Rammstein)
Makros in C sind ja so eine Sache.
Zum einen kommt man in C an vielen Stellen gar nicht ohne aus, da sie die einzige Möglichkeit sind Ausdrücke OHNE sie zu evaluieren an mehreren stellen zu verwenden. Zum anderen Sind sie rein Textuell, "unhygienisch" und haben keine Ahnung was in ihnen eigentlich passiert.
Das macht vor allem in Objective-C mächtig Schwierigkeiten. Das hier zum Beispiel geht nicht.
#define DO(argument) argument DO([NSString stringWithFormat:@"%s", "asdf"]);
Der Compiler meckert hier ĂĽber zu viele Kommas und denkt das ich das Makro mit zwei Argumenten aufrufen will. Gnah.
Warum beschäftigt mich das eigentlich?
Ich möchte gerne Macro's haben, die je nach Typ des Arguments etwas anderes machen. Für Unit-Tests z.B. möchte ich sagen:
- (void) testOverloadedAssert { ASSERT_EQUALS(3, 3); ASSERT_EQUALS(@"barf", @"barf"); ASSERT_EQUALS("bar", "bar"); // These should fail in a sensible manner ASSERT_EQUALS("bar", @"bar"); ASSERT_EQUALS(3, 3.3); }
Das ist aber schon ein großes Problem. In C++ könnte man den ersten teil ja noch über überladene Funktionen lösen - das geht in reinem Objective-C aber schon mal nicht. Dazu kommt ja noch der zweite Teil - da kriegt man vielleicht noch Compiler-Fehlermeldungen, wenn der Compiler nicht auto-conversion von Parametern anwirft. :-/
Dazu kommt, das ich C++ nicht mag und schon gar nicht in alle meine Tests eine zwingende Abhängigkeit auf C++ einfügen will.
Will man dann aber noch weiter gehen, wird es endgĂĽltig schwierig. Hier ein paar Beispiele:
- (void) testAdvancedAssert { ASSERT_EQUALS(3.3, 3.3, 0.1 /* accuracy */); ASSERT_EQUALS(3.3, 3.3, @"important meta information"); ASSERT_EQUALS(3, 3, @"info contains: %@ in %s:%d", @"other info", __PRETTY_FUNCTION__, __LINE__); }
In reinem C hätte man hier gar keine Möglichkeiten mehr etwas zu machen.
Wie weit ich mit einer Lösung dennoch gekommen bin - morgen. :)
Schnell noch aus der Kategorie "Geek-Humor".
FĂĽr alle die die Prometheus Seite noch nicht besucht haben.
GroĂźartig.
My first complete piece of work for Nova Media. I did this after working there for about a month.
Fone2Phone allows you to have a one stop solution to transferring everything from your cell phone to your macintosh. From there on you can either put it on your iPhone or iPod (Touch), just use it on your System or sync it to a different cell phone.
As far as the technology is concerned Fone2Phone is a subset of our more capable application FoneLink which allows you to do all this and more. Check it out!
Just two days ago I watched the episode "Wie man sich täuschen kann" of Quarks und Co. (Which has a very nice Video Podcast - by the way)
They had this beautiful illusion - but in an animated form, which I have never seen before.
Well, after two hours, I had a screen-saver running and really like the results.
So, here you go too. :)
Supreme Geek-Unterhaltung aus dem Vortrag [wiki:2008/02/05/22.00 50 of 50].
"Lest mehr Code!" werde ich mir immer als Schlusswort des Literarischen Code-Quartetts merken.
Und weil man ja auch verdauen muss was man liest, empfiehlt sich natĂĽrlich guter Code - an dem man auch noch mehr lernen kann.
Daher war ich begeistert, dass O'Reilly das Buch "Beautiful Code" (blog) herausbrachte. Das geniale daran: Es werden nicht nur gute Programme abgedruckt, sondern Erklärungen, was hervorragende Programmierer daran schön finden und warum!
Und was fĂĽr Perlen da drin sind.
Besonders haben mir gefallen (ich bin noch nicht durch)
Und so geht es grade weiter.
Gibts auf Amazon oder einem Tracker in deiner Nähe.