Hierarchisches switch-case

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Manchmal hat man einige Cases in einem switch-case, die logisch zusammen gehören, und zu teilen gemeinsamen Code haben. In solchen Fällen wird der Code oft dupliziert.

Beispiel eines "flachen" switch-case mit Code-Duplizierung:

switch(ch){
  case '(':
    int type = PARENTESES;
    bool open = true; // Code in allen öffnenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;
  case '[':
    int type = SQUARE;
    bool open = true; // Code in allen öffnenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;
  case '{':
    int type = CURLY;
    bool open = true; // Code in allen öffnenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;

  case ')':
    int type = PARENTESES;
    bool open = true; // Code in allen schliessenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;
  case ']':
    int type = SQUARE;
    bool open = true; // Code in allen schliessenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;
  case '}':
    int type = CURLY;
    bool open = true; // Code in allen schliessenden Klammern
    printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
    break;
}

Das Pattern des Hierarchischen switch-case dient dazu, solche code Duplizierung zu vermeiden, ohne zusätzliche Logik hinzuzufügen. Hierfür muss der Control Flow angepasst werden. In C können wir uns hier zunutze machen, das ein case-label auch in Unterblöcken auftauchen kann. Zudem können wir sicherstellen, dass ein Branch nur über das Label erreichbar ist, wenn es sich in einem `if(0)` befindet.

Beispiel Hierarchischen switch-case:

int type;
bool open;
switch(ch){
  if(0){
    if(0){ case '(': type = PARENTESES; }
    if(0){ case '[': type = SQUARE; }
    if(0){ case '{': type = CURLY; }
    open = true; // Code in allen öffnenden Klammern
  }
  if(0){
    if(0){ case ')': type = PARENTESES; }
    if(0){ case ']': type = SQUARE; }
    if(0){ case '}': type = CURLY; }
    open = false; // Code in allen schliessenden Klammern
  }
  printf("%d %d\n", open, type); // Code bei allen öffnenden und Schliessenden Klammern
  break;
}

Wie man sieht, ist der Code kompakter, mit massiv weniger Dupliziertem Code. Wo man hier etwas aufpassen muss, ist, die Variablen vor dem switch-case zu definieren. Eine Variable zu benutzen, dessen Deklaration übersprungen wurde (also im switch, aber vor dem case label), wäre UB.

Viele Entwickler haben Vorbehalte, dieses Pattern zu verwenden, da es entgegen diverse coding-best-practices usw. verstösst, wie z.B. das Springen in Unterblöcke des Codes, sowie des Springen in einen ansonsten nicht erreichbaren code-block `if(0)`, was von einigen Entwicklern manchmal als alternatives Pattern zum Auskommentieren von Code verwendet wird.

In der Praxis sind dies aber keine echten Probleme. Das case folgt direkt auf das `if(0)`, das macht das Pattern leicht erkennbar, und vom Auskommentierungspattern unterscheidbar. Ausserdem hat jeder Case und jeder Codebereich nur einen entry-point. Der Control-flow ist weiterhin sehr simpel, von oben nach unten, linear, und in klare Bereiche unterteilt.

Hier ein Diagramm, um die Eleganz des control-flows zu demonstrieren: Hirarchisches switch-case 3.png