Des switch-case plus jolis

Article original : Prettier switch-cases | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre

J’ai appris cette syntaxe au cours d’une présentation de la CppCon de 2021, donnée par Herb Sutter : Extending and Simplifying C++: Thoughts on Pattern Matching using `is` and `as` – Herb Sutter – YouTube. On peut aussi retrouver sa présentation sur son blog : Sutter’s Mill – Herb Sutter on software development.

Contexte

Mettons que vous ayez un bloc switch-case sans plongeon1 (c’est important), comme ceci) :

enum class Foo {
    Alpha,
    Beta,
    Gamma,
};
 
int main()
{
    std::string s;
    Foo f;
 
    // ...
    // Do things with s and f 
    // ...
 
    switch (f)
    {
        case Foo::Alpha:
            s += "is nothing";
            break;
        case Foo::Beta:
            s += "is important";
            f = Foo::Gamma;
            break;
        case Foo::Gamma:
            s += "is very important";
            f = Foo::Alpha;
    }
     
    // ...
}

Rien de particulièrement compliqué ici : on ajoute un suffixe à la chaîne en fonction de la valeur de f, parfois en modifiant f au passage.

Maintenant, mettons qu’on ajoute un littéral Delta à l’énumération Foo, qui fait exactement la même chose que Gamma avec une petite différence dans la chaîne. Il y a une bonne chance pour qu’on écrive ceci :

enum class Foo {
    Alpha,
    Beta,
    Gamma,
    Delta,
};
 
int main()
{
    std::string s;
    Foo f;
 
    // ...
    // Do things with s and f 
    // ...
 
    switch (f)
    {
        case Foo::Alpha:
            s += "is nothing";
            break;
        case Foo::Beta:
            s += "is important";
            f = Foo::Alpha;
            break;
        case Foo::Gamma:
            s += "is very important";
            f = Foo::Alpha;
        case Foo::Delta:
            s += "is not very important";
            f = Foo::Alpha;
    }
 
    // ...
}

Le nouveau bloc case est certainement du copier-coller. Mais avez-vous remarqué le bug qui s’est introduit ?

Comme dans première version, le développeur n’a pas jugé nécessaire de rajouter un break à la fin du bloc Gamma (puisque c’était le dernier élément), quand on a copié-collé ce bloc on l’a laissé sans break. Du coup, quand on va passer dans le code de Gamma, on va toujours plonger dans le code de Delta.

Nouvelle syntaxe

La syntaxe présentée dans cet article permet (entre autre) de rendre ce genre de fautes moins fréquent, et de faire en sorte que le code soit un peu plus clair.

La voici :

switch (f)
{
    break; case Foo::Alpha:
        s += "is nothing";
    break; case Foo::Beta:
        s += "is important";
        f = Foo::Alpha;
    break; case Foo::Gamma:
        s += "is very important";
        f = Foo::Alpha;
    break; case Foo::Delta:
        s += "is not very important";
        f = Foo::Alpha;
}

Cela consiste à mettre l’instruction break; juste avant chaque case.

Cela peut sembler étrange au premier regard, car le tout premier break est inutile et il n’y en a pas à la fin, mais cette syntaxe est fonctionnelle et surtout confortable.

En effet, si vous commencez tous vos blocs avec un break; case XXX:, vous êtes garantis de ne jamais faire de plongeon, même avec des hordes de copier-coller.

Avantages

Le premier avantage est celui d’éviter le genre de bug que j’ai mentionné plus haut, où on oublie un break ce qui cause un plongeon indésirable. Et même si vous ne faites pas de copier-coller, l’oubli du break sera visuellement évident puisque votre case ne sera pas aligné avec les autres.

Mais le réel avantage (d’après moi) est que cette syntaxe est, dans sa globalité, visuellement plus agréable. Pour chaque case, on économise une ligne de code (où devrait être le break; normalement), et il sera évident pour quiconque regarde le code que ce switch-case ne contient aucun plongeon.

Bien sûr, la beauté est subjective, y compris en programmation2. Cependant, des choses comme un meilleur alignement, des intentions plus claires et l’économie de lignes3 sont, il me semble, des critères plutôt objectifs.

Avertissement

La première fois que j’ai vu cette syntaxe, j’ai rapidement compris son fonctionnement et son intérêt. Je sais ceci dit qu’il y a plusieurs personnes qui ont dû se les faire expliquer.

Mais c’est presque toujours le cas quand on introduit une nouvelle syntaxe.

Gardez-donc à l’esprit que si vous voulez l’utiliser dans un code partagé, elle risque d’embrouiller vos collègues. Soyez-sûr·e de bien l’expliquer (que ce soit en personne ou en commentaire) pour qu’elle soit rapidement prise en main.

En conclusion

Ça ne va certainement pas changer votre vie quotidienne ni même votre vision du C++, mais je voulais la partager, car je l’aime beaucoup.

C’est une brique de plus dans la construction d’un code plus agréable à lire.

Merci de votre attention et à la semaine prochaine !

Article original : Prettier switch-cases | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre

Addendum

Notes

  1. Le terme « plongeon » est utilisé ici comme traduction de « fallthrough » en anglais, qui dans le contexte d’un switch-case renvoie au fait qu’on peut se dispenser du mot-clé break pour qu’un bloc case « plonge » dans un autre, comme dans l’exemple suivant :
switch (a)
{
    case 0:
        ++b; // Dans le cas où a == 0, on fait ++b puis on plonge dans le cas 1
    case 1:
        ++b;
        break; // Ici il y a un break, donc on ne plonge pas dans le cas par défaut
    default:
        --b;
}
  1. Surtout en programmation, oserai-je même dire.
  2. L' »économie de lignes » n’est une bonne chose que lorsqu’elle défausse des expressions peu instructives, comme c’est le cas avec une ligne qui contient uniquement l’instruction break;. Jamais vous ne m’entendrez dire que les gros one-liners sont préférables à un code plus détaillés (parce qu’ils ne le sont tout simplement pas). Réunir le break et le case laisse le code respirer. De plus, vous pouvez toujours laisser une ligne vide à l’endroit où était le break auparavant — votre code n’en sera que plus agréable.

Laisser un commentaire