Forum: Mikrocontroller und Digitale Elektronik C enum typedef Frage


von dings (Gast)


Lesenswert?

Hallo,

mir tat sich gerade folgende Frage auf:
Warum funktionieren Beispiel 1 und 2 im gegensatz zu BSP 3 ohne Warnung?
1
typedef enum
2
{
3
   eFlag0 = (1 << 0),
4
   eFlag1 = (1 << 1),
5
   eFlag2 = (1 << 2)
6
}eFlag_t;
7
8
9
void flag_fkt(eFlag_t eFlag)
10
{
11
// BSP 1
12
   flag_fkt(eFlag0 | eFlag1 | eFlag2);
13
14
// BSP 2
15
   if( eFlag == (eFlag0 | eFlag1 | eFlag2) )
16
   {}
17
   else if ( eFlag == (eFlag0 | eFlag1) )
18
   {}
19
20
// BSP3  (warning: case value "*" not in enumarated type eFlag_t)
21
   switch( eFlag )
22
   {
23
      case (eFlag0 | eFlag1 | eFlag2):
24
      break;
25
26
      case (eFlag0 | eFlag1):
27
      break;
28
   }
29
}

Habe diese Weise den enum typedef als Funktionsparameter zu nutzen von 
der LPCopen Bib übernommen. Fand ich ganz praktisch weil man dann sofort 
sehen kann was zu übergeben ist und man nicht aus einer Liste von 
defines aussuchen muss. Gedanken hierzu wären mir auch willkommen.

Grüße und bitte seid lieb

von Cyblord -. (cyblord)


Lesenswert?

Ein enum ist einfach nicht dafür da, irgendwelche Flags zu speichern.
Ein enum soll eine Aufzählung an Werten bieten und sorgt dafür dass 
diese Werte eine Namen haben, gruppiert sind und vom Compiler geprüft 
werden können.
Der konkrete Wert steht nicht im Mittelpunkt eines enums. Auch wenn sie 
einen Wert zugewiesen haben.

Bitte einfach mal überlegen ob es Sinn macht, Flags da rein zu stopfen. 
Vielleicht erinnerst du dich dass es auch ganz normale Variablen gibt, 
die haben auch einen Namen. Niemand muss defines nehmen wenn er nicht 
will.

: Bearbeitet durch User
von Hoschti (Gast)


Lesenswert?

dings schrieb:
> // BSP 1
>    flag_fkt(eFlag0 | eFlag1 | eFlag2);

Kann es sein, dass hier ein Tippfehler drin ist? Du rufst hier die 
Funktion von innen heraus nochmal auf.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ohne den Hintergrund zu kennen, macht es vielleicht Sinn bool Typen zu 
deklarieren und im Switch Case bewusst Falltrough ausnutzen. Dann hätte 
man sowas wie eine ODER Verknüpfung.

von dings (Gast)


Lesenswert?

Die Rekursion mit dem aufruf von flag_fkt() ergibt natürlich keine Sinn, 
ist auch nur ein Beispiel wie man den enum typedef "missbrauchen" kann.

Hätte einfach gerne eine Reihe von Flags (auch mehr als 3) die nur dort 
definiert sind wo eben der typedef bekannt ist und das auch am 
Funktionsprototyp ersichtlich ist welche Flags in Kombination der 
Funktion zu übergeben sind.

Das gibts dann wohl einfach nicht?

von dings (Gast)


Lesenswert?

Nachtrag:
Jetzt hab ich noch mal was rumprobiert und die Warnung bekomm ich weg 
indem ich "switch( (int)eFlag )" schreibe.

(Ach wie oft bin ich schon zwischen c ist toll und c ist kacke hin und 
her geschwankt)

von Cyblord -. (cyblord)


Lesenswert?

dings schrieb:
> Nachtrag:
> Jetzt hab ich noch mal was rumprobiert und die Warnung bekomm ich weg
> indem ich "switch( (int)eFlag )" schreibe.
>
> (Ach wie oft bin ich schon zwischen c ist toll und c ist kacke hin und
> her geschwankt)

Du programmierst Kacke. Das allein ist dein Problem. Sorry das kann C 
nun echt nichts dafür.

Du Vergewaltigst den Switch/Case für dein Flag Verorderung. Und 
beschwerst dich noch? So ist das ganze einfach nicht gedacht.

Du willst auf KOMBINATIONEN von Flags prüfen. Das kann dein enum aber 
gar nicht.
Er kann nur einen Wert der Liste annehmen. Keine Kombinationen.

: Bearbeitet durch User
von dings (Gast)


Lesenswert?

ja hast ja recht Cyblord. Dann überleg ich mir mal ob ichs kacke mache 
oder eben defines nehme. Vermisse einfach das es neben einem "enum" auch 
ein "flag" gibt.

Hab das sonst auch nie so gemacht. Hab mir das letztens eben mal 
abgeguckt und fands ganz praktisch weil ich so die API sofort 
durchschauen konnte wie in welche Funktion Flags zu übergeben sind.

von Cyblord -. (cyblord)


Lesenswert?

dings schrieb:
> ja hast ja recht Cyblord. Dann überleg ich mir mal ob ichs kacke mache
> oder eben defines nehme.

Wie wärs mit richtig machen?

> Vermisse einfach das es neben einem "enum" auch
> ein "flag" gibt.

Mit "Flags" sind ja eigentlich konstante Bitmasken gemeint. Richtig? Du 
kannst dafür entweder defines nehmen oder const Variablen.
Wieso sollte hier was fehlen?

> Hab das sonst auch nie so gemacht. Hab mir das letztens eben mal
> abgeguckt und fands ganz praktisch weil ich so die API sofort
> durchschauen konnte wie in welche Funktion Flags zu übergeben sind.

Flags als uint irgendwas zu übergeben hat sich bewährt. Machs einfach 
so.

Wenn dir das zu low level ist, dann mache high level funktionen die eben 
nicht direkt flags bekommen, sondern z.B. ganze Structs mit Bools drin.

Die sind mehr als einfach zu durchschauen. Damit abstrahierst du über 
die Flags und deine Funktion baut die Flags aus den Bools und sonstigen 
Werten der Struct zusammen. Der Benutzer der Funktion muss dann nichts 
über die Flags oder deren Bitpositionen wissen.
Das ist die beste Lösung.

: Bearbeitet durch User
von dings (Gast)


Lesenswert?

ja ok mach ich.
Das mit Structs zu machen ist auch kein schlechter hinweis - Danke.

von Klaus W. (mfgkw)


Lesenswert?

dings schrieb:
> Das mit Structs zu machen ist auch kein schlechter hinweis - Danke.

Bitfelder wäre hier wahrscheinlich das richtige Stichwort.

von Cyblord -. (cyblord)


Lesenswert?

Klaus W. schrieb:
> dings schrieb:
>> Das mit Structs zu machen ist auch kein schlechter hinweis - Danke.
>
> Bitfelder wäre hier wahrscheinlich das richtige Stichwort.

Nein.

Nur wenn du um jedes Byte Speicher kämpfst.

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Mal ausgeschrieben das ganze:

>von dings schrieb:
1
typedef enum
2
{
3
   eFlag0 = 1,
4
   eFlag1 = 2,
5
   eFlag2 = 4
6
}eFlag_t;
7
void flag_fkt(int eFlag)
8
{
9
// BSP 1
10
   flag_fkt(7);
11
// BSP 2
12
   if( eFlag == 7 )
13
   {}
14
   else if ( eFlag == 3 )
15
   {}
16
// BSP3  (warning: case value "*" not in enumarated type eFlag_t)
17
   switch( eFlag )
18
   {
19
      case (7):
20
      break;
21
      case (3):
22
      break;
23
   }
24
}
Ist das wirklich das was du willst?

von Hans-Georg L. (h-g-l)


Lesenswert?

dings schrieb:
> Hallo,
>
> mir tat sich gerade folgende Frage auf:
> Warum funktionieren Beispiel 1 und 2 im gegensatz zu BSP 3 ohne Warnung?
>
Bei Beispiel 1 sollte ein anständiger Compiler schon aussteigen weil das 
Ergebnis der Veroderung ein int und kein eFlag_t ist was du übergibst.
Der C Compiler kann nur enum values zu int konvertieren aber nicht 
umgekehrt.
Warum packst du nicht alle zulässigen Kombinationen mit ins enum dann 
brauchst du keine Klimmzüge machen.

von Noch ein Kommentar (Gast)


Lesenswert?

> Sorry das kann C nun echt nichts dafür.

Die Sache hat durchaus einen gewissen Humor.

Kernighan & Ritche entwickelten C gerade deswegen, weil sie in einer 
Hochsprache solche Bitfummeleien machen wollten.

Im Prinzip hast du recht. C kann nichts dafür. Schuld sind die Leute, 
die seit 40 Jahren immer wieder neue Features an C dranpappen.

von Piter K. (kurczaq)


Lesenswert?

da fehlt einfach ein

default:
  break;

von Klaus W. (mfgkw)


Lesenswert?

wer wird denn hier auf die Frage antworten?

von Rolf M. (rmagnus)


Lesenswert?

Hans-Georg L. schrieb:
> Warum packst du nicht alle zulässigen Kombinationen mit ins enum dann
> brauchst du keine Klimmzüge machen.

Das können aber ziemlich viele werden, wenn's z.B. 10 Flags sind und 
prinzipiell jede Kombination möglich wäre.

dings schrieb:
> Das mit Structs zu machen ist auch kein schlechter hinweis - Danke.

Das macht halt die Nutzung ziemlich umständlich, weil man dann nicht 
mehr einfach sowas schreiben kann wie:
1
funktion(FlagA | FlagD | FlagF);

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

Rolf M. schrieb:
> Hans-Georg L. schrieb:
>> Warum packst du nicht alle zulässigen Kombinationen mit ins enum dann
>> brauchst du keine Klimmzüge machen.
>
> Das können aber ziemlich viele werden, wenn's z.B. 10 Flags sind und
> prinzipiell jede Kombination möglich wäre.
>
Dann kannst du dir aussuchen einen Fehler in einem zentralen enum mit 
1024 Einträgen oder in 1024 im Programm verstreuten Funktionaufrufen zu 
suchen.
Mit einem enum zeigt dir schon der Compiler eine nicht zulässige 
Kombination sonst bekommst du (hoffentlich) eine Laufzeit Fehlermeldung 
z.B. aus einem default Zweig einer switch Anweisung und das vielleicht 
erst beim Kunden weil es selten auftritt.

Beispiele für viele Kombinationen sind Fehlercodes, Handles, IDs, da 
kommt auch kein Mensch auf die Idee das mit Bit Veroderung zu machen da 
nimmt man  zentrale enum und gut ist.

: Bearbeitet durch User
von Noch ein Kommentar (Gast)


Lesenswert?

> da nimmt man  zentrale enum und gut ist.

Ja. Guter Standpunkt.

Mag ja sein, die Leute, die den C Standard erweiterten, hatten das so 
nicht vorgesehen. Trotzdem ist es ein gutes und sinnvolles Design 
Pattern.

Das meiste funktioniert ja mit dem automatischen enum nach int cast. Ab 
und zu muss man halt explizit casten.

Wir sollten diese Diskussion an die ISO weiter leiten. Damit wir im 
nächsten C Standard offiziell enums als Bitfelder nutzen können.

von Hans-Georg L. (h-g-l)


Lesenswert?

Noch ein Kommentar schrieb:
>> da nimmt man  zentrale enum und gut ist.
>
> Ja. Guter Standpunkt.
>
> Mag ja sein, die Leute, die den C Standard erweiterten, hatten das so
> nicht vorgesehen. Trotzdem ist es ein gutes und sinnvolles Design
> Pattern.
>
> Das meiste funktioniert ja mit dem automatischen enum nach int cast. Ab
> und zu muss man halt explizit casten.
>
> Wir sollten diese Diskussion an die ISO weiter leiten. Damit wir im
> nächsten C Standard offiziell enums als Bitfelder nutzen können.

Die werden für diese Idee ein mildes lächeln übrig haben ;-) Keine 
Angst, da wurde nichts verpasst oder verschlafen ;-)

In C musst du mit den Bitfeldern in Strukturen und ihren Nachteilen 
leben.
In C++ gibt es Bitfelder in der Stdlib aber auch (noch) nicht ideal, für 
diesen Zweck, geeignet ...

Was ich mir im C++ Standard wünsche wäre die Abfrage während der 
Compilezeit ob ein Wert im enum enthalten ist oder nicht. Wird aber 
schon diskutiert ...

von A. S. (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Dann kannst du dir aussuchen einen Fehler in einem zentralen enum mit
> 1024 Einträgen oder in 1024 im Programm verstreuten Funktionaufrufen zu
> suchen.
Warum das?

> Mit einem enum zeigt dir schon der Compiler eine nicht zulässige
> Kombination sonst bekommst du (hoffentlich) eine Laufzeit Fehlermeldung
Wenn Flags unabhängig sind, dann werden genau dies vorkommen. Wenn es 
keine Flags sind, dann sollte man auch keine verwenden

> Beispiele für viele Kombinationen sind Fehlercodes, Handles, IDs, da
> kommt auch kein Mensch auf die Idee das mit Bit Veroderung zu machen da
> nimmt man  zentrale enum und gut ist.
Entweder man hat einen Skalar (enum, int, handle, ...) oder Flags 
(bitfelder, Masken, Kombinationen, ...).  Für beide gibt es gute 
Praktiken. Man sollte das aber nicht verwechseln.

von Ein Kommentar (Gast)


Lesenswert?

Erinnert ihr euch noch an den Spruch von Andrew Tannenbaum?

Enum-Bitfelder waren ein Fortschritt gegenüber allen vorherigen und 
allen nachfolgenden Lösungen.

von Hans-Georg L. (h-g-l)


Lesenswert?

A. S. schrieb:
> Hans-Georg L. schrieb:
>> Dann kannst du dir aussuchen einen Fehler in einem zentralen enum mit
>> 1024 Einträgen oder in 1024 im Programm verstreuten Funktionaufrufen zu
>> suchen.
> Warum das?
 weil er von 10 bit flags geschrieben hat ...

von A. S. (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> A. S. schrieb:
>> Hans-Georg L. schrieb:
>>> Dann kannst du dir aussuchen einen Fehler in einem zentralen enum mit
>>> 1024 Einträgen oder in 1024 im Programm verstreuten Funktionaufrufen zu
>>> suchen.
>> Warum das?
>  weil er von 10 bit flags geschrieben hat ...

Warum "1024 im Programm verstreute Funktionsaufrufe"? Im Extremfalls 
sind es 10 stellen, an denen 10 Flags gesetzt werden, und x Masken die 
Ausgewertet werden.

Oft erschlägt das erste Flag (an/aus) schon eine Hälfte der 
Kombinationen, und das nächste (aktiv/idle) vom Rest noch eine Hälfte. 
Trotzdem können alle vorkommen (aus & Error & Overrun), egal ob gewollt 
(zur Diagnose) oder als Fehler

von Hans-Georg L. (h-g-l)


Lesenswert?

A. S. schrieb:

> Warum "1024 im Programm verstreute Funktionsaufrufe"? Im Extremfalls
> sind es 10 stellen, an denen 10 Flags gesetzt werden, und x Masken die
> Ausgewertet werden.
>
> Oft erschlägt das erste Flag (an/aus) schon eine Hälfte der
> Kombinationen, und das nächste (aktiv/idle) vom Rest noch eine Hälfte.
> Trotzdem können alle vorkommen (aus & Error & Overrun), egal ob gewollt
> (zur Diagnose) oder als Fehle
Mit 10bit kannst du zahlen von 0.. 1023 darstellen ...

von Oliver S. (oliverso)


Lesenswert?

Hans-Georg L. schrieb:
> Bei Beispiel 1 sollte ein anständiger Compiler schon aussteigen weil das
> Ergebnis der Veroderung ein int und kein eFlag_t ist was du übergibst.
> Der C Compiler kann nur enum values zu int konvertieren aber nicht
> umgekehrt.

Nö. enums in C sind zwar eigene Typen, aber kompatibel zu int (oder 
einem anderen Ganzzahl-Typ), und das in alle Richtungen.

In C++ ist das anders, aber ein anständiger C-Compiler wird sich 
weigern, C++-Code zu übersetzen.

Oliver

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Mit 10bit kannst du zahlen von 0.. 1023 darstellen

Ja. Darum wäre so ein enum Wahnsinn. Zu Anfang hättest Du für mich den 
Eindruck erweckt, als fändest Du das besser als Flags.

von Mombert H. (mh_mh)


Lesenswert?

Oliver S. schrieb:
> Hans-Georg L. schrieb:
>> Bei Beispiel 1 sollte ein anständiger Compiler schon aussteigen weil das
>> Ergebnis der Veroderung ein int und kein eFlag_t ist was du übergibst.
>> Der C Compiler kann nur enum values zu int konvertieren aber nicht
>> umgekehrt.
>
> Nö. enums in C sind zwar eigene Typen, aber kompatibel zu int (oder
> einem anderen Ganzzahl-Typ), und das in alle Richtungen.

Um das etwas klarer zu machen:
1
6.7.2.2 Enumeration specifiers
2
[...]
3
4) Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined,130) but shall be capable of representing the values of all the members of the enumeration.
Das hilft aber erstmal nicht viel bei der Konvertierung. Anders dagegen
1
6.2.5 Types
2
[...]
3
14) The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types.
Es gibt Regeln für die Konvertierung zwischen verschiedenen "integer 
types".

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Hans-Georg L. schrieb:
> A. S. schrieb:
>
>> Warum "1024 im Programm verstreute Funktionsaufrufe"? Im Extremfalls
>> sind es 10 stellen, an denen 10 Flags gesetzt werden, und x Masken die
>> Ausgewertet werden.
>>
>> Oft erschlägt das erste Flag (an/aus) schon eine Hälfte der
>> Kombinationen, und das nächste (aktiv/idle) vom Rest noch eine Hälfte.
>> Trotzdem können alle vorkommen (aus & Error & Overrun), egal ob gewollt
>> (zur Diagnose) oder als Fehle
> Mit 10bit kannst du zahlen von 0.. 1023 darstellen ...

Mit 10 Bit kann ich 10 Flags darstellen. Der Vorschlag, jede der 
möglichen 1024 Kombination dieser Flags getrennt zu behandeln, kam von 
dir.
Nehmen wir mal als Beispiel das Öffnen einer Datei. Da gibt's vielleicht 
ein Flag, mit dem man sagt, dass sie zum lesen geöffnet werden soll, 
eins zum schreiben, eins um das Caching zu deaktivieren, eins um 
anzugeben, ob ein anderes Programm die Datei gleichzeitig auch öffnen 
darf u.s.w.
Da will ich halt für jedes der 10 Flags eine Stelle, wo ich das handhabe 
und nicht eine für jede der 1024 Kombinationen aus den Flags.

von Adam P. (adamap)


Lesenswert?

Rolf M. schrieb:
> Da will ich halt für jedes der 10 Flags eine Stelle, wo ich das handhabe
> und nicht eine für jede der 1024 Kombinationen aus den Flags.

Kommt drauf an...
Will man logische Zuordnungen oder Speicher sparen?

10 Flags = 10 Bit = mindestens uint16_t (2 Byte)
10 Zustände = 4 Bit = uint8_t (und noch eine Menge mehr Platz)

Wenn du wenig Speicher hast, dann speicherst du eine Zahl die das 
gleiche wie ein Flag aussagt, nicht als einzelne Stellen, sondern als 
Wert.

Bsp:
// Nutzt 6 Byte
char a[] = {'1', '2', '3', '4', '5', '6'};
vs.
// Nutzt 4 Byte
uint32_t a = 123456;

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Adam P. schrieb:
> 10 Flags = 10 Bit = mindestens uint16_t (2 Byte)
> 10 Zustände = 4 Bit = uint8_t (und noch eine Menge mehr Platz)

10 Flags und 10 Zustände sind so verschieden wie Äpfel und Sonnentage. 
Es macht wenig Sinn, das zu verwischen.

von Hans-Georg L. (h-g-l)


Angehängte Dateien:

Lesenswert?

Rolf M. schrieb:
> Hans-Georg L. schrieb:
>> Warum packst du nicht alle zulässigen Kombinationen mit ins enum dann
>> brauchst du keine Klimmzüge machen.
>
> Das können aber ziemlich viele werden, wenn's z.B. 10 Flags sind und
> prinzipiell jede Kombination möglich wäre.
>

Du hattest geschrieben jede Kombination möglich ...
und darauf hatte ich geantwortet ...
lass gut sein ...

Aber da die typedef enums in C keine "echten Datentypen" sind ...
macht es überhaupt keinen Sinn einen typedef enum als Parameter zu 
benutzen.
Weil man da lustig alles reinhauen kann was nach int konvertierbar ist.

Ich bleibe bei meinem C++ und hasse ab sofort C enums ;-)

von Veit D. (devil-elec)


Lesenswert?

Hans-Georg L. schrieb:

> Was ich mir im C++ Standard wünsche wäre die Abfrage während der
> Compilezeit ob ein Wert im enum enthalten ist oder nicht. Wird aber
> schon diskutiert ...

Gibts schon. Stichwort enum class ...

von A. S. (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Aber da die typedef enums in C keine "echten Datentypen" sind ...
> macht es überhaupt keinen Sinn einen typedef enum als Parameter zu
> benutzen.
> Weil man da lustig alles reinhauen kann was nach int konvertierbar ist.

Na eben nicht. Darum dreht sich doch der thread. Dass der Compiler da 
meckert.

Also Du bestimmst das in C wie du willst.

von Klaus W. (mfgkw)


Lesenswert?

A. S. schrieb:
> Na eben nicht. Darum dreht sich doch der thread.

Ich dachte, um die Frage ganz oben. :-)

> Dass der Compiler da
> meckert.

Bei mir nicht, mein gcc übersetzt das ohne Murren.
(mit -std=c90 stört er sich an den C++-Kommentaren, aber das ist ein 
anderes Thema).

Zu der Frage, ob man das so machen sollte (case (eFlag0 | eFlag1 | 
eFlag2)...):
Wenn man sich im Klaren darüber ist, was man macht und es zur Anwendung 
passt - warum nicht. Auch wenn es nicht jedem gefällt.

Tipp:
Man kann sich auch gleich das enum entsprechend zuschneidern:
1
typedef enum
2
{
3
   eFlag0 = (1 << 0),
4
   eFlag1 = (1 << 1),
5
   eFlag2 = (1 << 2),
6
   eFlagAlleDrei = eFlag0|eFlag1|eFlag2
7
}eFlag_t;
Dann geht auch ein case eFlagAlleDrei...

Daß man so etwas in C++ evtl. schöner formulieren kann, geschenkt...

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Hans-Georg L. schrieb:
> Rolf M. schrieb:
>> Hans-Georg L. schrieb:
>>> Warum packst du nicht alle zulässigen Kombinationen mit ins enum dann
>>> brauchst du keine Klimmzüge machen.
>>
>> Das können aber ziemlich viele werden, wenn's z.B. 10 Flags sind und
>> prinzipiell jede Kombination möglich wäre.
>>
>
> Du hattest geschrieben jede Kombination möglich ...

Das heißt aber nicht, dass ich jede Kombination getrennt verarbeiten 
will. In der Regel benutze ich ja Flags, weil ich mehrere Dinge damit 
angeben will, die mehr oder weniger unabhängig von einander sind. Daher 
interessieren mich die vielen möglichen Kombinationen gar nicht. Mich 
interessiert jedes Flag für sich alleine. Gerade deshalb halte ich es ja 
für wenig sinnvoll, einen eigenen Namen für jede einzelne dieser 
Kombinationen zu vergeben.

> Aber da die typedef enums in C keine "echten Datentypen" sind ...
> macht es überhaupt keinen Sinn einen typedef enum als Parameter zu
> benutzen.

Das typedef kannst du weglassen, das hat damit nichts zu tun. Und warum 
sollten enums keine echten Datentypen sein oder nicht geeignet für 
Parameter?

> Weil man da lustig alles reinhauen kann was nach int konvertierbar ist.

Es gibt eine implizite Konvertierung von und nach enum-Typen. Ideal ist 
das nicht, das stimmt.

von A. S. (Gast)


Lesenswert?

Klaus W. schrieb:
> A. S. schrieb:
>
>> Na eben nicht. Darum dreht sich doch der thread.
>
> Ich dachte, um die Frage ganz oben. :-)
>> Dass der Compiler da
>> meckert.
>
> Bei mir nicht, mein gcc übersetzt das ohne Murren

Eben. Jeder wie er mag. Wer "Typsicherheit" will, lässt Warnungen werfen 
wie beim UP Bsp.3 (die Frage des TO).

Warnungen wenn ints statt enums zugewiesen werden oder in einem switch 
nicht alle cases oder default auftaucht.

Und im dependency-walk bei lint kann man sich auch warnen lassen, wenn 
ein enum per cast einen Wert außerhalb bekommen soll. Natürlich nur in 
den Grenzen, die statische Codeanalyse hat.

von Oliver S. (oliverso)


Lesenswert?

A. S. schrieb:
> Eben. Jeder wie er mag. Wer "Typsicherheit" will, lässt Warnungen werfen
> wie beim UP Bsp.3 (die Frage des TO).

Die Warnung dort besagt ja eigentlich, daß der eine case-Block nie 
ausgeführt würde, wenn da ein "echter" enum im select benutzt wird, weil 
der case-Wert im enum gar nicht definiert ist.

Da ist die Warnung dann schon angebracht.

Oliver

von Cyblord -. (cyblord)


Lesenswert?

Oliver S. schrieb:
> Die Warnung dort besagt ja eigentlich, daß der eine case-Block nie
> ausgeführt würde, wenn da ein "echter" enum im select benutzt wird, weil
> der case-Wert im enum gar nicht definiert ist.

Was auch gleich direkt zum Problem des Code führt. Ich weiß auch nicht 
was es da so lange zu diskutieren gibt:
Es macht keinen Sinn auf Kombinationen von enums zu prüfen, weil ein 
enum immer nur einen Wert annehmen kann. Keine Kombination von enum 
Werten.

Es handelt sich einfach um ein massives Missverständnis der gesamten 
Einrichtung "enum".

von Mombert H. (mh_mh)


Lesenswert?

Cyblord -. schrieb:
> Es macht keinen Sinn auf Kombinationen von enums zu prüfen, weil ein
> enum immer nur einen Wert annehmen kann. Keine Kombination von enum
> Werten.
Wo genau steht das?

von Cyblord -. (cyblord)


Lesenswert?

Mombert H. schrieb:
> Cyblord -. schrieb:
>> Es macht keinen Sinn auf Kombinationen von enums zu prüfen, weil ein
>> enum immer nur einen Wert annehmen kann. Keine Kombination von enum
>> Werten.
> Wo genau steht das?

Das entspricht dem Prinzip von enums. Natürlich kannst du die auf jeden 
Wert casten. Aber wenn deine enum Variable einen Wert annimmt, der nicht 
im enum definiert ist, bist du außerhalb des Konzepts enum angekommen.

von Klaus W. (mfgkw)


Lesenswert?

Cyblord -. schrieb:
> Es handelt sich einfach um ein massives Missverständnis der gesamten
> Einrichtung "enum".

DEINEM Verständnis von enum mag es widersprechen.

Aber letztlich sind enums einfach benamste ganzzahlige Konstanten und 
ein eleganterer Ersatz für #define-Orgien.
Zu wissen, was dabei in welchem Bit steht und das zu nutzen ist genauso 
legitim oder verwerflich wie bei allen anderen ganzzahligen Werten auch.

In den meisten Fällen wird man mit enum einfach irgendwas abzählen. Das 
muß aber nicht immer sein, sonst bräuchte man nicht die Möglichkeit, 
beliebige Werte zuzuweisen.
Ob das in diesem Fall dann sinnvoll ist, sei mal dahingestellt. Aber 
Aussagen wie "mit enum darf man nur abzählen" kommt man nicht weit 
außerhalb vom Einhorngehege.

In C ist es nun mal leider nicht mehr und man muß es nehmen wie es ist.
In C++ kann man echte Typen draus machen; hilft hier aber halt nicht 
weiter, wenn man bei C bleiben will.

von Mombert H. (mh_mh)


Lesenswert?

Cyblord -. schrieb:
> Mombert H. schrieb:
>> Cyblord -. schrieb:
>>> Es macht keinen Sinn auf Kombinationen von enums zu prüfen, weil ein
>>> enum immer nur einen Wert annehmen kann. Keine Kombination von enum
>>> Werten.
>> Wo genau steht das?
>
> Das entspricht dem Prinzip von enums. Natürlich kannst du die auf jeden
> Wert casten. Aber wenn deine enum Variable einen Wert annimmt, der nicht
> im enum definiert ist, bist du außerhalb des Konzepts enum angekommen.
Es entspricht nicht deiner Vorstellung von enums, das ist etwas anderes.

von Cyblord -. (cyblord)


Lesenswert?

Klaus W. schrieb:
> Cyblord -. schrieb:
>> Es handelt sich einfach um ein massives Missverständnis der gesamten
>> Einrichtung "enum".
>
> DEINEM Verständnis von enum mag es widersprechen.

Ja sicher ich kann hier nur meine Meinung dazu wiedergeben.

> Aber letztlich sind enums einfach benamste ganzzahlige Konstanten und
> ein eleganterer Ersatz für #define-Orgien.

Dem würde ich nicht zustimmen. Man kann sie dafür verwenden. Aber man 
sollte nicht.

> Aussagen wie "mit enum darf man nur abzählen" kommt man nicht weit
> außerhalb vom Einhorngehege.

Wie weit man kommt wenn man meint enum einfach irgendwie zu verwenden 
sieht man an obigem Code.

von Peter D. (peda)


Lesenswert?

Formal sind nur die Werte zugelassen, die im Enum definiert sind. Das 
Switch will auf beiden Seiten den gleichen Typ und warnt daher.
Das == macht implizit eine Konvertierung nach int und warnt daher nicht.

von Hans-Georg L. (h-g-l)


Lesenswert?

Veit D. schrieb:
> Hans-Georg L. schrieb:
>
>> Was ich mir im C++ Standard wünsche wäre die Abfrage während der
>> Compilezeit ob ein Wert im enum enthalten ist oder nicht. Wird aber
>> schon diskutiert ...
>
> Gibts schon. Stichwort enum class ...

Lies nochmal was ich geschrieben habe ... wie Bitte kann ich ein class 
enum während der Compilezeit fragen ob ein bestimter Wert darin 
enthalten/definiert ist ist oder nicht ? Ich kann eine Fehlermeldung 
erzeugen aber keine Abfrage ohne Abbruch.

von Mombert H. (mh_mh)


Lesenswert?

Peter D. schrieb:
> Formal sind nur die Werte zugelassen, die im Enum definiert sind. Das
> Switch will auf beiden Seiten den gleichen Typ und warnt daher.
> Das == macht implizit eine Konvertierung nach int und warnt daher nicht.
Meinst du mit formal zugelassen den Standard? Wo steht das deiner 
Meinung nach? Und switch macht auch in integer promotions.

von Cyblord -. (cyblord)


Lesenswert?

Mombert H. schrieb:
> Meinst du mit formal zugelassen den Standard? Wo steht das deiner
> Meinung nach?

Keine Ahnung ob das wirklich im Standard steht. Aber mein Anspruch ist 
immer Clean Code und nicht "gerade noch im Standard puh alles gut".

von Mombert H. (mh_mh)


Lesenswert?

Cyblord -. schrieb:
> Mombert H. schrieb:
>> Meinst du mit formal zugelassen den Standard? Wo steht das deiner
>> Meinung nach?
>
> Keine Ahnung ob das wirklich im Standard steht. Aber mein Anspruch ist
> immer Clean Code und nicht "gerade noch im Standard puh alles gut".
Das kann man auch umdrehen: "Hauptsache es sieht gut aus, egal ob vom 
Standard erlaubt" ;-)

von Cyblord -. (cyblord)


Lesenswert?

Mombert H. schrieb:
> Das kann man auch umdrehen: "Hauptsache es sieht gut aus, egal ob vom
> Standard erlaubt" ;-)

Kann man. Wäre aber falsch. Der Standard ist die untere Grenze aber ich 
würde den Anspruch deutlich höher ansiedeln.

von Peter D. (peda)


Lesenswert?

Mombert H. schrieb:
> Meinst du mit formal zugelassen den Standard? Wo steht das deiner
> Meinung nach?

Eine Stelle konnte ich nicht finden.
Dafür aber ein Beispiel für das Verodern von Enums:
https://www.programiz.com/c-programming/c-enumeration

Da scheint also doch keine Einigkeit zu herschen.

von Hans-Georg L. (h-g-l)


Lesenswert?

A. S. schrieb:
> Hans-Georg L. schrieb:
>> Aber da die typedef enums in C keine "echten Datentypen" sind ...
>> macht es überhaupt keinen Sinn einen typedef enum als Parameter zu
>> benutzen.
>> Weil man da lustig alles reinhauen kann was nach int konvertierbar ist.
>
> Na eben nicht. Darum dreht sich doch der thread. Dass der Compiler da
> meckert.
>
> Also Du bestimmst das in C wie du willst.

ich bestimme nichts ...
wenn ich eine funktion definiere :
void fkt(my_enum value);
und rufe die mit:
fkt(0.5) auf
ist es für mich schon überraschend wenn das ohne Fehler compiliert ...

In C++ benutze ich solche Funktionen um sicher zu stellen das nur im 
enum definierte Werte als Parameter zulässig sind und habe 
fälschlicherweise angenommen, das wäre in C genau so. Wieder was gelernt 
;-)

von Cyblord -. (cyblord)


Lesenswert?

Peter D. schrieb:
> Da scheint also doch keine Einigkeit zu herschen.

Weil man enums grundsätzlich ohne Problem verodern kann. Nur wenn man 
das in einem Switch/Case versucht, springt einen das Problem förmlich 
an. Davor aber halt nicht.

Der Switch/Case Fall des TE ist auch nochmal was besonderes, weil er 
hier ein übergebenes enum explizit auf eine Kombination von enums testen 
will. Was absurd falsch ist.

Will ich aber nur irgendeinen Wert auf bestimmte Flags testen, und diese 
Flags sind zufällig enums, geht das ohne Verrenkungen. Auch wenn es 
nicht so schön ist. Es versursacht aber kein grundlegendes Problem.

: Bearbeitet durch User
von Mombert H. (mh_mh)


Lesenswert?

Peter D. schrieb:
> Mombert H. schrieb:
>> Meinst du mit formal zugelassen den Standard? Wo steht das deiner
>> Meinung nach?
>
> Eine Stelle konnte ich nicht finden.
> Dafür aber ein Beispiel für das Verodern von Enums:
> https://www.programiz.com/c-programming/c-enumeration
>
> Da scheint also doch keine Einigkeit zu herschen.
Wieso besteht da keine Einigkeit?

von Noch ein Kommentar (Gast)


Lesenswert?

Wieso besteht hier keine Einigkeit?

Anscheinend treffen hier zwei grundsätzlich unvereinbare Standpunkte 
aufeinander.

Die einen meinen: Im C Standard ist der Missbrauch von enums als 
Bitfelder nicht vorgesehen. Deswegen dürfen wir enums auch nicht als 
Bitfelder benutzen.

Die anderen meinen: Enums als Bitfelder missbrauchen ist immer noch die 
beste aller Lösungen. Auch wenn der automatische enum nach int cast bei 
ein paar Konstruktionen nicht funktioniert.

P.S. ich bin der Ansicht, das eigentliche Problem ist - wenn wir ein 
paar Libraries einbinden, bekommen wir in einem Programm alle Varianten, 
die in den 40 Jahren mal modern waren. Müssen zwischen #define, enum, 
struct int:1, usw. hin und her konvertieren.

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.