SucheBlog abonnierenVerwaltung des BlogsKategorienKontaktMarkus Brückner |
Dienstag, 28. Juni 2005Heisse Kontakte?Ich habe zu Experimentierzwecken auf meinem Compaq (mittlerweile HP) iPaq ein Familiar Linux mit Opie-Umgebung installiert. Dazu habe ich beispielsweise noch das Programm kopi, eine Portierung des KOrganizer auf diese Umgebung gepackt und wollte eben einen Termin eintragen. Nun hat mich doch etwas ein wenig verwundert:
Aha, ich kann also "Heisse Kontakte" eintragen. Das muss wohl der Grund sein, wieso immer wieder Männer von ihren Frauen beim Fremdgehen erwischt werden. Die sind einfach so doof und tragen das in ihren Terminkalender ein! Etwas später wurde mir allerdings klar, daß ich mich geirrt hatte und das alles ganz anders gemeint war. Der Kontakt wird allein durch seinen Ort heiß! Wie konnte ich auch an was anderes denken?!
Freitag, 10. Juni 2005Erweiterung der Template-SpielereienDie Logging-Klasse, die bereits länger existiert, hat nun noch ein paar Verbesserungen erfahren. Zuersteinmal stellte sich beim Einsatz unter Windows ein Fehler heraus. Wenn man nämlich die Klasse in einem Windows-Programm unter Visual Studio verwendet, so kommt es beim Kompilieren im Debug-Modus zu merkwürdigen Fehlermeldungen. Diese beziehen sich bspw. auf die Deklaration der Loglevel. Bei genauerem Hinsehen stellt sich folgendes heraus: Obwohl ich die gesamte Klasse in einen eigenen Namensraum gepackt habe, um Kollisionen mit anderen Paketen zu vermeiden, passiert genau das. Die Namen DEBUG, TRACE und ERROR sind bereits vergeben. Diese werden als Makros vom VS Präprozessor definiert und dienen zur Ausgabe von Debug-Informationen. Da sich der Präprozessor nicht um Namensräume schert, ersetzt er die entsprechenden Token im Quelltext natürlich, was der Compiler richtigerweise mit Syntaxfehlern quittiert. Daher tragen die Level nun alle den Präfig LEVEL_. Das macht leider den Code etwas umfangreicher, vermeidet aber Fehler. Eine weitere Sache, die sich beim Debugging von Code immer recht praktisch macht ist eine Angabe, wo im Quelltext ein Fehler auftaucht. Für diesen Zweck enthält der ANSI-C(und damit auch der C++)-Standard die Makros __FILE__ und __LINE__. Diese werden zur Compilezeit zum aktuellen Dateinamen und der aktuellen Zeilennummer aufgelöst, wo sie auftauchen. Da das recht praktisch ist, findet sich jetzt im Headerfile ein Makro namens HERE, welche zur Compilezeit zu dem String "dateiname:zeile" aufgelöst wird. Um diesen konstanten String zu erhalten muss man noch auf einen kleinen Trick zurückgreifen, da __LINE__ normalerweise als Zahl aufgelöst wird. Ein einfaches Anhängen an den String in __FILE__ ist somit nicht möglich. Zu diesem Zweck definiert man sich folgendes Konstrukt: #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define HERE __FILE__":"TOSTRING(__LINE__)" " Das Makro STRINGIFY gibt seinen Parameter als String aus. Dieser Parameter kommt wiederum von TOSTRING. Die zweifache Kette von Makros wird notwendig durch die spezifischen Eigenschaften des #-Operators des Präprozessors. Dieser gibt den ihm folgenden Parameter als String aus. Würde man hier nun direkt STRINGIFY(__LINE__) aufrufen, so lautete die Ausgabe "__LINE__", da der Parameter eben so heißt. Durch die Indirektion über TOSTRING passiert folgendes: Im ersten Schritt wird der Aufruf TOSTRING(__LINE__) durch STRINGIFY(zahl) ersetzt. zahl entspricht dabei der aktuellen Zeilennummer. Danach wird dieses Konstrukt rekursiv durch die Stringdarstellung des Parameters von STRINGIFY ersetzt; es entsteht also der Wert "zahl". Diesen kann man nun ganz einfach mit den restlichen Strings verketten lassen. Eine letzte Kleinigkeit ergibt sich aus der Notwendigkeit, Logger auch dynamisch anlegen zu können. Der bisher favorisierte Ansatz, den Logger als globale Variable zu definieren und so vor dem Eintreten in main zu initialisieren, erreicht seine Grenzen, wenn bspw. das Ziel der Loggingausgabe zur Laufzeit erst festgelegt werden soll (bspw. durch einen Kommandozeilenparameter). Dazu muss man in der Lage sein, den Logger innerhalb des normalen Programmflusses dynamisch anzulegen. Hier schafft die Definition einiger zusätzlicher Ausgabeoperatoren Abhilfe. Diese nehmen statt einer Referenz auf einen Logger einen Zeiger auf diesen entgegen, dereferenzieren diesen und rufen darauf dann ihr passendes Gegenstück auf. Dessen Rückgabe (nämlich eine Referenz auf den Logger, auf den der übergebene Pointer zeigt) liefern sie anschließend zurück. Durch diese Operatoren werden Konstruktionen wie die folgende möglich:
Logging::Logger *logger;
int main(int argc, char *argv[])
{
logger = new Logger(std::ofstream(argv[1]);
logger << LEVEL_DEBUG << "A debug message" << Logging::endl;
delete logger;
return 0;
}
Dieses Programm bindet den Logger auf den Dateinamen, den es als erstes Argument bekommt. Theoretisch ist so sogar ein Wechsel des Loggingziels möglich, indem der Logger mittels delete zerstört und danach mit new neu angelegt wird. Den Quellcode kann man natürlich wieder hier runterladen.
(Seite 1 von 1, insgesamt 2 Einträge)
|