====== Enums — Aufzählungstypen ====== Ein ''enum'' definiert einen Typ, dessen gültige Werte eine abgeschlossene, benannte Menge bilden. Der Compiler kennt alle Werte zur Compile-Zeit — das ist die Grundlage der Exhaustivitätsprüfung in ''match'': Wird ein Enum-Wert nicht behandelt, ist es ein **Fehler**, keine Warnung. Enums sind in Lyx ganzzahlige Typen (intern ''int64'') mit benannten Konstanten. Sie haben keine Methoden und keine Vererbung — ihr einziger Zweck ist die typsichere Zustandsdarstellung. → [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching]] · [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] · [[lyx_-_programmiersprache:guides:do-178c|DO-178C]] ---- ===== 1. Deklaration ===== // Einfachster Fall: Werte beginnen bei 0 und zählen aufwärts enum Direction { North, South, East, West } // North=0, South=1, East=2, West=3 // Explizite Werte — ganze Zahlen im Bereich 0..4294967295 enum HttpStatus { Ok = 200, Created = 201, NotFound = 404, ServerErr = 500 } // Gemischt: erste Konstante setzt den Startwert, die folgenden zählen weiter enum Priority { Low = 1, Medium, // = 2 High, // = 3 Critical // = 4 } Regeln: * Der Wertebereich ist ''0..4294967295'' (uint32). Negative Werte und alles darüber werden abgewiesen: ''enum E: der Wert von A liegt ausserhalb von 0..4294967295''. Ein Enum trägt also **keine** beliebigen int64-Werte. * Gleiche Namen in **verschiedenen** Enums sind unproblematisch — der Qualifier trennt sie (''E1.X'' und ''E2.X''). * Enums dürfen leer sein (''enum Empty {}'' — selten sinnvoll, aber erlaubt). * ''%%enum E { A, A }%%'' — derselbe Name doppelt — wird abgewiesen. * ''%%enum E { A = 1, B = 1 }%%'' — zwei Namen auf denselben Wert — ist dagegen erlaubt: als Alias ist das oft gewollt. ---- ===== 2. Verwendung ===== Ein einfaches Anwendungsbeispiel: enum Color { Red, Green, Blue } // Variablendeklaration var c: Color := Color.Red; // Übergabe als Parameter fn PrintColor(col: Color): void { match (col) { case Color.Red => Print("Rot\n"); case Color.Green => Print("Grün\n"); case Color.Blue => Print("Blau\n"); } } // Rückgabe aus Funktion fn OppositeDirection(d: Direction): Direction { return match (d) { case Direction.North => Direction.South; case Direction.South => Direction.North; case Direction.East => Direction.West; case Direction.West => Direction.East; }; } Der Zugriff auf einen Enum-Wert erfolgt immer mit ''EnumName.Wert'' — der Namespace-Qualifier ist Pflicht. Das verhindert Namenskollisionen zwischen verschiedenen Enums. Ein Tippfehler im Member-Namen ist ein Compile-Fehler: ''Color.Rrd'' wird abgewiesen, nicht stillschweigend zu ''0''. ---- ===== 3. Enums und match ===== Die Stärke von Enums zeigt sich in Kombination mit ''match'': Der Compiler erzwingt, dass **jeder** mögliche Wert behandelt wird. enum FlightPhase { Preflight, Taxiing, Takeoff, Climbing, Cruise, Descending, Approach, Landing, Rollout } fn PhaseToString(p: FlightPhase): pchar { return match (p) { case FlightPhase.Preflight => "Vor dem Start"; case FlightPhase.Taxiing => "Rollen"; case FlightPhase.Takeoff => "Start"; case FlightPhase.Climbing => "Steigflug"; case FlightPhase.Cruise => "Reiseflug"; case FlightPhase.Descending => "Sinkflug"; case FlightPhase.Approach => "Anflug"; case FlightPhase.Landing => "Landung"; case FlightPhase.Rollout => "Ausrollen"; }; } Wird später ''FlightPhase.GoAround'' hinzugefügt, meldet der Compiler: sema error (line 3): match does not cover variant 'GoAround' — add a case or a wildcard _ => { ... } Das ist eine **Sicherheitsgarantie**: Neue Zustände können nicht unbehandelt durchrutschen. ==== Wann default erlaubt ist ==== ''default'' unterdrückt die Exhaustivitätsprüfung. Das ist explizit erlaubt, aber sparsam einsetzen: fn IsErrorStatus(s: HttpStatus): bool { return match (s) { case HttpStatus.ServerErr => true; case _ => false; // Explizite Entscheidung: alle nicht genannten Werte sind "kein Fehler" // Risiko: neuer Wert wie HttpStatus.GatewayTimeout wird stillschweigend false }; } > In DO-178C DAL-A/B-Code ist ''default'' bei zustandsbehafteten Enums ein Compiler-Fehler wenn ''@dal(A)'' oder ''@dal(B)'' gesetzt ist. Alle Zustände müssen explizit behandelt sein. ---- ===== 4. Enum als Integer ===== Intern ist jeder Enum-Wert ein ''int64''. Die explizite Konvertierung erfolgt mit ''as'': enum HttpStatus { Ok = 200, NotFound = 404, ServerErr = 500 } // Enum → int64 var code: int64 := HttpStatus.Ok as int64; // 200 // int64 → Enum (unsafe: der Compiler prüft den Wert nicht) var s: HttpStatus := 404 as HttpStatus; // In der Praxis: Wert aus Netzwerk empfangen, in Enum konvertieren fn ParseStatus(raw: int64): HttpStatus { return match (raw) { case 200 => HttpStatus.Ok; case 404 => HttpStatus.NotFound; case 500 => HttpStatus.ServerErr; case _ => HttpStatus.ServerErr; // Fallback bei unbekanntem Code }; } > Die Konvertierung ''intVal as HttpStatus'' ist technisch möglich, aber gefährlich: Der Wert ''999'' wäre ein ungültiger ''HttpStatus''. In ''@dal(A)''-Code ist der direkte Cast von ''int64'' zu einem Enum verboten — stattdessen muss ''ParseStatus'' oder eine vergleichbare Funktion mit explizitem ''match'' verwendet werden. ---- ===== 5. Bit-Flag-Enums ===== Für Bit-Masken werden Enums mit Zweierpotenzen als Werte deklariert und mit Integer-Operatoren kombiniert: enum Permission { Read = 0x01, // Bit 0 Write = 0x02, // Bit 1 Execute = 0x04, // Bit 2 Admin = 0x08 // Bit 3 } // Mehrere Flags kombinieren (als int64 — nicht als Permission) var perms: int64 := Permission.Read as int64 | Permission.Write as int64; // Flag prüfen fn HasPermission(perms: int64, flag: Permission): bool { return (perms & (flag as int64)) != 0; } fn main(): int64 { var p: int64 := Permission.Read as int64 | Permission.Execute as int64; if (HasPermission(p, Permission.Write)) { PrintLn("Schreiben erlaubt"); } else { Print("Kein Schreibzugriff\n"); // Dieser Zweig wird ausgeführt } return 0; } Bit-Flag-Enums werden immer als ''int64'' kombiniert — Lyx bietet keinen speziellen Bitset-Typ. Das ist ein bewusstes Design: Bitoperationen sind explizit und direkt im Code sichtbar. ==== Masken als Konstante ==== Seit **1.0.20A** gelten Enum-Werte auch in ''con''-Ausdrücken und in Startwerten globaler Variablen (#1507). Damit lässt sich eine Maske einmal benennen, statt sie an jeder Prüfstelle neu zusammenzusetzen: enum P { Read = 1, Write = 2, Exec = 4 } con MASK: int64 := P.Read as int64 | P.Write as int64; var perms: int64 := P.Read as int64; fn main(): int64 { PrintLn("MASK=", IntToStr(MASK), " perms=", IntToStr(perms)); return 0; } MASK=3 perms=1 Die **Vorwärtsreferenz** ist zulässig — das ''enum'' darf unter der ''con''-Zeile stehen, die es benutzt. **Vor 1.0.20A ergab genau dieses Muster still ''0''.** Die ''con''-Zeile übersetzte ohne Warnung, ''MASK'' war aber 0 — und damit jede Prüfung ''(wert & MASK) != 0'' immer falsch. Wer eine ältere Fassung benutzt, prüft seine Masken nach; der Fehler ist am Quelltext nicht zu sehen. ---- ===== 6. Enums in Datenstrukturen ===== Enums als Felder in Structs und als Array-Elemente: import std.alloc; enum State { Idle, Running, Paused, Stopped } // Enum-Feld in einem alloc'd Puffer (Offset-Muster) pub con TASK_SIZE: int64 := 24; pub con TASK_STATE: int64 := 0; // State als int64 gespeichert pub con TASK_ID: int64 := 8; pub con TASK_PRIO: int64 := 16; fn TaskCreate(id: int64, prio: int64): int64 { var t: int64 := alloc(TASK_SIZE); if (t == 0) { return 0; } poke64(t + TASK_STATE, State.Idle as int64); poke64(t + TASK_ID, id); poke64(t + TASK_PRIO, prio); return t; } fn TaskGetState(t: int64): State { return peek64(t + TASK_STATE) as State; } fn TaskSetState(t: int64, s: State): void { poke64(t + TASK_STATE, s as int64); } fn TaskIsRunning(t: int64): bool { return TaskGetState(t) == State.Running; } ---- ===== 7. Enums in Safety-Code ===== Enums sind ein bevorzugtes Mittel in sicherheitskritischem Code, weil sie den Zustandsraum explizit und abgeschlossen definieren: @flight_crit(DAL-A) enum EngineState { Off = 0, Starting = 1, Idle = 2, Running = 3, Overspeed = 4, Shutdown = 5 } @flight_crit(DAL-A) fn EngineTransition(current: EngineState, cmd: EngineCommand): EngineState { // Vollständige Zustandsmaschine — kein default, alle Kombinationen explizit return match (current) { case EngineState.Off => match (cmd) { ... }; case EngineState.Starting => match (cmd) { ... }; case EngineState.Idle => match (cmd) { ... }; case EngineState.Running => match (cmd) { ... }; case EngineState.Overspeed => EngineState.Shutdown; // immer herunterfahren case EngineState.Shutdown => EngineState.Off; }; } ^ Eigenschaft ^ Enum ^ int64-Konstanten ^ | Compiler-Exhaustivitätsprüfung | ✅ Ja | ❌ Nein | | Typsicherheit im Parameter | ✅ Ja | ❌ Nein (jede int64 passt) | | Dokumentation im Code | ✅ Explizite Namen | Eingeschränkt | | Gültigkeitsbereich | Pro Enum getrennt | Global | | DO-178C geeignet | ✅ Empfohlen für Zustände | Bedingt | ---- ===== 8. Zusammenfassung ===== ^ Frage ^ Antwort ^ | Basistyp | ''int64'' (immer) | | Zugriffssyntax | ''EnumName.Wert'' | | Methoden | Nicht unterstützt | | Enum → int64 | ''val as int64'' | | int64 → Enum | ''intVal as EnumTyp'' (unsafe — Wert unklar) | | match-Exhaustivität | Pflicht ohne ''default'' | | In Safety-Code | Enums für alle Zustände bevorzugen, kein ''default'' | | Bit-Flags | Werte als Zweierpotenzen; kombinieren als ''int64'' | → [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching — match, Guards, Exhaustivität]]\\ → [[lyx_-_programmiersprache:sprache:datentypen|Datentypen — vollständige Typübersicht]]\\ → [[lyx_-_programmiersprache:sprache:typaliase-und-typumwandlung|Typ-Aliase und Typumwandlung (as, is)]] Letzte Aktualisierung: 2026-08-09 Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).