Forum: Compiler & IDEs Define in Enumeration?


von Klasss (Gast)


Lesenswert?

Hallo!

In dem Header zu epoll (epoll.h; z.B. hier 
http://www.danga.com/memcached/dist/epoll.h) bin ich über folgende 
Enumeration gestoßen:
1
enum EPOLL_EVENTS
2
  {
3
    EPOLLIN = 0x001,
4
#define EPOLLIN EPOLLIN
5
    EPOLLPRI = 0x002,
6
#define EPOLLPRI EPOLLPRI
7
    EPOLLOUT = 0x004,
8
#define EPOLLOUT EPOLLOUT
9
    EPOLLRDNORM = 0x040,
10
#define EPOLLRDNORM EPOLLRDNORM
11
    EPOLLRDBAND = 0x080,
12
#define EPOLLRDBAND EPOLLRDBAND
13
    EPOLLWRNORM = 0x100,
14
#define EPOLLWRNORM EPOLLWRNORM
15
    EPOLLWRBAND = 0x200,
16
#define EPOLLWRBAND EPOLLWRBAND
17
    EPOLLMSG = 0x400,
18
#define EPOLLMSG EPOLLMSG
19
    EPOLLERR = 0x008,
20
#define EPOLLERR EPOLLERR
21
    EPOLLHUP = 0x010,
22
#define EPOLLHUP EPOLLHUP
23
    EPOLLET = (1 << 31)
24
#define EPOLLET EPOLLET
25
  };

Was haben den diese #defines in der Enumeration zu suchen und warum 
macht man so etwas überhaupt?

Danke & Grüße
Klasss

von Rolf M. (rmagnus)


Lesenswert?

Meine Vermutung wäre: Damit man per #ifdef prüfen kann, ob bestimmte 
Elemente existieren.

von eagle user (Gast)


Lesenswert?


von Rolf M. (rmagnus)


Lesenswert?

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?

von meckerziege (Gast)


Lesenswert?

Wo liegt das Problem? Das define sieht eh nur der präprozessor. Danach 
ists weg.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von eagle user (Gast)


Lesenswert?

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.

von Achim M. (minifloat)


Lesenswert?

Dem Präprozessor sind die Enum-Elemente und auch deren Zahlenwert 
bekannt.
Sowas hier...
1
typedef enum
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
typedef uint8_t MyBitGeFummelType;
19
#elif(MyEnumNumOf <= 16)
20
typedef uint16_t MyBitGeFummelType;
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
MyBitGeFummelType MyVar;
..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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Achim M. (minifloat)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

Joachim K. schrieb:
> Mein Konstrukt tut schon was es soll.

Setz doch mal ein #ifndef vor deiner Abfrage.
1
...
2
#if !defined(MyEnumNumOf)
3
#error Argh!
4
#elif(MyEnumNumOf <= 8)
5
...
Mit gcc bekomme ich da "Argh!". ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
typedef uint8_t MyBitGeFummelType;

verwendet.

Der GCC verhält sich genauso, wie eben beschrieben. Welchen Compiler
verwendest du?

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Achim M. (minifloat)


Lesenswert?

Yalu X. schrieb:
> Welchen Compiler
> verwendest du?

Cosmic, der frisst das so...

von Rolf M. (rmagnus)


Lesenswert?

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?

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:

1
#error cpcorm mytest.c:4 "THREE hat den Wert 0"

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.