meckerziege schrieb:> Wo liegt das Problem? Das define sieht eh nur der präprozessor. Danach> ists weg.
Warum dann C/C++/Assembler? Das sieht eh nur Compiler/Assembler, danach
ist's weg.
meckerziege schrieb:> Wo liegt das Problem?
Es geht um die glibc, die in verschiedenen Versionen vorliegen kann, und
nicht alle Elemente des obigen enum gibt es in jeder Version. Die
#defines sorgen dafür, dass ein Programm mit den unterschiedlichen
Versionen zusammen funktionieren kann, indem es per #ifdef nur die
Elemente benutzt, die auch tatsächlich verfügbar sind.
Man kann in C nicht Teile des Code abhängig davon weglassen, ob ein
bestimmtes enum-Element exisitert oder nicht, aber mit Makros geht das.
meckerziege schrieb:> Das define sieht eh nur der präprozessor.
Genau für den ist es ja auch bestimmt.
Rolf M. schrieb:> eagle user schrieb:>> Für mich ist das ein weiterer Grund, #define zu vermeiden.>> Wie würdest du dieses Problem denn ohne #define lösen?
Möglicherweise hätte es dieses Problem nie gegeben, wenn niemand
"#define EPOLLIN 1" benutzt hätte. Jetzt geht es wohl nicht mehr ohne.
Für die ganzen CONFIG_FOO im Kernel ist #if und #define wohl auch
praktisch, das sehe ich schon ein. Aber in meinen Programmen möchte ich
den preprocessor möglichst sparsam nutzen.
Dem Präprozessor sind die Enum-Elemente und auch deren Zahlenwert
bekannt.
Sowas hier...
1
typedefenum
2
{
3
MyEnumFoo,
4
MyEnumBar,
5
MyEnum2,
6
MyEnum3,
7
MyEnum4,
8
MyEnum5,
9
MyEnum6,
10
MyEnum7,
11
MyEnum8,
12
MyEnum9,
13
MyEnumA,
14
MyEnumNumOf
15
}MyEnumType;
16
17
#if(MyEnumNumOf <= 8)
18
typedefuint8_tMyBitGeFummelType;
19
#elif(MyEnumNumOf <= 16)
20
typedefuint16_tMyBitGeFummelType;
21
#else
22
#error Mehr als 16bit mag ich jetz nicht
23
#endif
24
25
/* Die Bits in MyVar haben jetzt Namen und MyVar hat die nötige Bitbreite */
26
MyBitGeFummelTypeMyVar;
..habe ich zur Zeit im Einsatz. Wäre ja doof, wenn das nicht kompiliert.
Ich wage aber die These aufzustellen, dass das nicht jeder Compiler so
frisst.
Rolf M. schrieb:> Man kann in C nicht Teile des Code abhängig davon weglassen, ob ein> bestimmtes enum-Element exisitert oder nicht, aber mit Makros geht das.
Man kann doch die korrespondierende Funktionalität totlegen und zB. in
einen Fehlerzustand gehen? Damit bleibt die Interface-definition (= das
Enum) gleich, es gibt halt nur nicht alles.
Schöner als im Ausgangsposting wäre es aber, die #define unter die
Enum-Definition zu packen.
mfg mf
eagle user schrieb:> Rolf M. schrieb:>> eagle user schrieb:>>> Für mich ist das ein weiterer Grund, #define zu vermeiden.>>>> Wie würdest du dieses Problem denn ohne #define lösen?>> Möglicherweise hätte es dieses Problem nie gegeben, wenn niemand> "#define EPOLLIN 1" benutzt hätte. Jetzt geht es wohl nicht mehr ohne.
Nein, darum geht es gar nicht. Es geht um die Erkennung, ob es überhaupt
existiert.
Sagen wir mal, in Version X gab es das enum-Element EPOLLERR noch nicht.
Das wurde in Version X+1 hinzugefügt. Ich muss jetzt ein Programm
schreiben, das dieses auf Version X+1 benutzt, aber auch fehlerfrei
durch den Compiler läuft, wenn ich Version X nutzen will. Ohne #define
kann ich nicht erkennen, ob es das enum-Element EPOLLERR gibt oder
nicht. Und wenn mein Programm es einfach benutzt, obwohl es nicht
existiert, bricht der Compiler mit Fehler ab.
Joachim K. schrieb:> Dem Präprozessor sind die Enum-Elemente und auch deren Zahlenwert> bekannt.
Nein.
> ..habe ich zur Zeit im Einsatz. Wäre ja doof, wenn das nicht kompiliert.
Das solltest du nochmal überdenken. Entferne aus deinem Beispiel mal den
enum komplett und probiere mal, was dann passiert.
> Ich wage aber die These aufzustellen, dass das nicht jeder Compiler so> frisst.
Sollte schon, nur tut es nicht das, was du erwartest.
> Rolf M. schrieb:>> Man kann in C nicht Teile des Code abhängig davon weglassen, ob ein>> bestimmtes enum-Element exisitert oder nicht, aber mit Makros geht das.> Man kann doch die korrespondierende Funktionalität totlegen und zB. in> einen Fehlerzustand gehen?
Dazu müsste man aber schon von Anfang an wissen, was in späteren
Versionen alles mal dazu kommen wird.
Rolf M. schrieb:>> ..habe ich zur Zeit im Einsatz. Wäre ja doof, wenn das nicht kompiliert.>> Das solltest du nochmal überdenken. Entferne aus deinem Beispiel mal den> enum komplett und probiere mal, was dann passiert.
Das Enum im Ursprungspost wird ja auch nie komplett entfernt, sondern
nur erweitert. Mein Konstrukt tut schon was es soll. Ich sehe im disasm,
mapfile, Debugger, ... , dass der Präprozessor den Wert vom numof wohl
doch kannte. Also muss auch der der anderen Enum-Elemente bekannt sein.
Ich werde das mal testen...
Rolf M. schrieb:> Ohne #define kann ich nicht erkennen, ob es das enum-Element EPOLLERR> gibt oder nicht.
Du kannst mit #if(x==y)\sonstwas\#endif prüfen, ob ein Ding namens
EPOLLERR den von dir erwarteten Wert besitzt. Wenn nicht, gibt es das
Feature in der Version der lib, wasweissich, offenbar noch nicht.
Joachim K. schrieb:> Das Enum im Ursprungspost wird ja auch nie komplett entfernt, sondern> nur erweitert. Mein Konstrukt tut schon was es soll. Ich sehe im disasm,> mapfile, Debugger, ... , dass der Präprozessor den Wert vom numof wohl> doch kannte.
Wenn der Präprozesssor das wirklich tut, also in
1
#if(MyEnumNumOf <= 8)
für MyEnumNumOf den Wert 11 einsetzt, verstößt er gegen die ISO-Norm,
die besagt:
"After all replacements due to macro expansion and the defined unary
operator have been performed, all remaining identifiers (including those
lexically identical to keywords) are replaced with the pp-number 0"
Da MyEnumNumOf kein Makro ist und deeswegen nicht expandiert werden
kann, zählt es zu den "remaining identifiers" und wird deswegen durch 0
ersetzt. Im #if-#elsif-#else-#endif-Konstrukt wird also wegen 0<=8
unabhängig von der Anzahl der Enum-Elemente immer die erste Alternative
1
typedefuint8_tMyBitGeFummelType;
verwendet.
Der GCC verhält sich genauso, wie eben beschrieben. Welchen Compiler
verwendest du?
Joachim K. schrieb:> Rolf M. schrieb:>>> ..habe ich zur Zeit im Einsatz. Wäre ja doof, wenn das nicht kompiliert.>>>> Das solltest du nochmal überdenken. Entferne aus deinem Beispiel mal den>> enum komplett und probiere mal, was dann passiert.>> Das Enum im Ursprungspost wird ja auch nie komplett entfernt, sondern> nur erweitert.
Es ging mir nicht um einen Vergleich zum Ursprungsposting. Wenn du aus
deinem Beispiel den enum komplett entfernst, wird es trotzdem noch
fehlerfrei compilieren.
> Mein Konstrukt tut schon was es soll. Ich sehe im disasm,> mapfile, Debugger, ... , dass der Präprozessor den Wert vom numof wohl> doch kannte.
Da siehst du nirgends, welchen Wert er eingesetzt hat.
> Ich werde das mal testen...
Das solltest du auf jeden Fall tun.
Joachim K. schrieb:> Schöner als im Ausgangsposting wäre es aber, die #define unter die> Enum-Definition zu packen.
Das ist so gleich die Dokumentation, welches Define zu welchem enum
gehört.
Oliver
Joachim K. schrieb:> Yalu X. schrieb:>> Welchen Compiler>> verwendest du?>> Cosmic, der frisst das so...
Nochmal: Dass er es "frisst", ist normal. Das sollte jeder Compiler tun.
Aber macht er auch wirklich das, was du dir vorstellst?
Ich habe der kostenlosen Demoversion des Cosmic-Compilers (COSMIC
Software CORTEX-M C Cross Compiler (LIMITED) V4.1.3) mal den folgenden
Code zum Fraß vorgeworfen:
1
enum{ZERO,ONE,TWO,THREE,FOUR};
2
3
#if THREE == 0
4
#error "THREE hat den Wert 0"
5
#elif THREE == 3
6
#error "THREE hat den Wert 3"
7
#else
8
#error "THREE hat einen anderen Wert"
9
#endif
Ruft man den Compiler ohne irgendwelche Optionen auf, gibt der Compiler
folgende Meldung aus:
1
#error cpcorm mytest.c:6 "THREE hat den Wert 3"
Wie von Joachim geschrieben, wird in der #if-Direktive für THREE also
tatsächlich die 3 aus dem Enum eingesetzt.
Lässt man aber nur den Präprozessor laufen (Compileroption -sp), lautet
die Meldung
1
#error cpcorm mytest.c:4 "THREE hat den Wert 0"
Dies zeigt, dass der Präprozessor alleine nicht in der Lage ist, die
Enum-Deklaration zu parsen. Vielmehr scheint im ersten Beispiel (ohne
Optionen) der Präprozessor und der eigentliche C-Parser so ineinander
verwoben zu sein, dass vom C-Parser Informationen (nämlich die Enum-
Werte) in den Präprozessor zurückfließen. Der Datenfluss zwischen
Präprozessor und C-Parser ist also – anders als bei anderen Compilern –
bidirektional.
Etwas seltsam ist es ja schon, dass die sequenzielle Ausführung von
Präprozessor- und Compiler-Phase zu einem anderen Ergebnis führt als die
Ausführung beider Phasen in einem einzelnen Aufruf.
Auch wenn die Auswertung von Enum-Werten im Präprozessor nicht der
ISO-Norm entspricht, scheint dieses Feature absichtlich implementiert
worden zu sein, denn mit der Option -psa (strict ANSI) verhält sich der
Compiler (egal, ob mit oder ohne -sp) wie erwartet: