js' blog

Input über glibc-Rant
Created: 26.02.2009 12:36 UTC

Grade eben erreicht mich eine e-mail mit der Anmerkung, mein glibc-Rant sei doch vollkommen unbegründet und die Manpage würde doch ganz klar sagen, wie man sie zu verwenden hätte.
Nun, bevor auch andere den Rant von damals falsch verstehen: Es geht hier nicht um C, es geht hier um Objective-C. Da hat #import durchaus seinen Grund. Und es hat Vorteile. Und daher ist das Argument, daß #import unportabel wäre, weil der MSVC++ es nicht könne, ziemlicher Blödsinn: MSVC++ kann Objective-C nämlich überhaupt nicht.

Da in dem Input über meinen glibc-Rant auch stand, daß #import doch nur Nachteile hätte und die Manpage ja auch #include nennen würde, scheint es hier wohl einige Unklarheiten zu geben, denn eigentlich hat es nur Vorteile. Ich will das mal hier erklären:
Jeder von euch kennt bestimmt das Problem, wenn man einen Header mehrfach includet. Nehmen wir einfach mal folgendes Beispiel:

foo.h:

extern int foo;

Wenn man das jetzt 2 mal includet (was häufig implizit vorkommt, weil sich Header gegenseitig auch includen), hat man zumindest eine Warnung, wenn nicht - je nach Compiler - sogar einen Fehler. Daher hat es sich eingebürgert, daß man folgendes schreibt:

foo.h:

#ifndef __FOO_H__
#define __FOO_H__
extern int foo;
#endif

Das muß man dann in jedem Header machen, was auch mitunter eine der meist kritisierten Sachen an C ist. Zumal es hier schon Probleme gibt, wenn 2 Header in verschiedenen Verzeichnissen den selben Namen haben. Dann muß man noch was anderes in den Define zur Identifikation packen, z.B. den Pfad oder den Namen der Library von der der Header ein Teil ist. Scheinbar waren auch die Leute von NeXT damals relativ genervt davon, weshalb sie eine neue Präprozessoranweisung einführten: #import.

Nun, wie aber unterscheidet sich #import von #include? Es ist eigentlich ganz einfach: Es macht das Konstrukt mit #ifdef und #define überflüssig, indem der Präprozessor sich merkt, ob das File bereits includet wurde. Ist das der Fall, macht #import einfach garnichts. Somit spart man sich die Konstruktion mit dem #ifdef und #define. Und ob ein anderer Header auch so heißt, darum muß man sich nicht kümmern, denn der Präprozessor weiß, um welchen Header es sich handelt.

So, was hat das aber mit dem glibc-Rant zu tun? Daß die glibc ziemlich kaputt ist, wenn man #import verwendet. Und zwar scheinen die Header gegenseitige Abhängigkeiten der Defines zu haben, sodaß einige Defines der glibc nicht passen und die glibc dann einige essentielle Sachen einfach nicht definiert. Die glibc kommt einfach mit ihren eigenen Defines durcheinander. Daher muß man dann für die glibc-Header immer #include nehmen statt #import. Was daran so schlimm ist? Man nutzt in Objective-C generell immer #import. Und es läuft auch problemlos auf allen Plattformen damit. Nur sowie es dann daran geht, daß man das ganze mit der glibc kompilieren möchte, hagelt es plötzlich Fehler. Fehler, die man sich auf Anhieb nicht erklären kann. Bis man irgendwann merkt, daß das Ändern der Reihenfolge, in der man importiert, das Problem behebt. Scheinbar. Bis es unter irgendwelchen ganz obskuren Umständen wieder bricht. Und dann sucht man wieder ewig. Also kann man nur #include nehmen, was dann doch sehr unschön ist, wenn man beides in einem File hat. Und wirklich jede andere libc kriegt das problemlos hin :). glibc hingegen versagt schon, wenn man nur wchar.h und string.h importiert im selben File.

So, ich hoffe, das sorgt jetzt für ein bisschen Aufklärung, was das Problem eigentlich wirklich ist. Und wie gesagt, es ist nur eines von Milliarden Problemen der glibc - eines der größten ist IMO das #define _GNU_SOURCE und das Fehlen von strl*, weil Drepper zu blöd für Strings ist.