<
Media
>
Article

Magic Enum : la révolution des enums en C++

7 min
29
/
04
/
2021

Aujourd'hui, il faut que je vous parle d'une bibliothèque qui a vraiment changé ma vie de développeur C++ au cours de la dernière année : Magic Enum. Elle est la création de Daniil Goncharov et est disponible sur Github sous licence MIT.

C'est une "header-only C++17 library" qui permet de faire la réflexion statique sur des enums.

Un bon exemple vaut mieux qu'un long discours :

<pre><code>#include &lt;iostream>
#include "magic_enum.hpp"

enum class MyEnum {
   HELLO = 42, WORLD = 99
};

int main() {
   constexpr auto enumName = magic_enum::enum_type_name&lt;MyEnum>();
   constexpr auto scoped = magic_enum::is_scoped_enum&lt;MyEnum>::value;
   std::cout << enumName << " is scoped = " << std::boolalpha << scoped << '\n';

   constexpr auto count = magic_enum::enum_count&lt;MyEnum>();
   std::cout << "It has " << count << " possible values :" << '\n';

   for (auto name : magic_enum::enum_names&lt;MyEnum>()) {
       std::cout << "- " << name << '\n';
   }

   std::cout << '\n';

   for (unsigned int i = 0; i < count; ++i) {
       auto value = magic_enum::enum_value&lt;MyEnum>(i);

       using namespace magic_enum::ostream_operators; // to pretty print the enum value
       std::cout << "Value = " << value << '\n';

       std::cout << "Integer = " << magic_enum::enum_integer(value) << '\n';
       std::cout << "Offset = " << magic_enum::enum_index(value).value() << '\n';

       auto name = magic_enum::enum_name(value);
       std::cout << "Name = " << name << '\n';

       std::cout << "---------" << '\n';
   }

   std::cout << '\n';
       std::cout << "Cast to enum:" << '\n';

   using namespace magic_enum::ostream_operators; // to print std::optional<MyEnum>

   constexpr auto invalid = magic_enum::enum_cast&lt;MyEnum>(0);
   constexpr  auto HELLO = magic_enum::enum_cast&lt;MyEnum>("HELLO");
   constexpr auto hello = magic_enum::enum_cast&lt;MyEnum>("hello");
   constexpr auto WORLD = magic_enum::enum_cast&lt;MyEnum>("WORLD");
   constexpr auto WORLD_AGAIN = magic_enum::enum_cast&lt;MyEnum>(99);

   std::cout << invalid << '\n';
   std::cout << HELLO << '\n';
   std::cout << hello << '\n';
   std::cout << WORLD << '\n';
}</code></pre>

Sortie :

<pre><code>MyEnum is scoped = true
It has 2 possible values :
- HELLO
- WORLD

Value = HELLO
Integer = 42
Offset = 0
Name = HELLO
---------
Value = WORLD
Integer = 99
Offset = 1
Name = WORLD
---------

Cast to enum:

HELLO

WORLD
WORLD</code></pre>

J'ai essayé de mettre autant que constexpr pour vous montrer qu'une bonne partie est faisable à la compilation, ce qui veut dire un coût nul à l'exécution. Comme j'ai une boucle, je ne peux pas mettre des constexpr partout mais si je choisis de prendre une valeur particulière de l'énumération, c'est possible :

<pre><code>int main() {
   using namespace magic_enum::ostream_operators;

   constexpr auto value = magic_enum::enum_cast&lt;MyEnum>("HELLO").value();

   constexpr auto integer = magic_enum::enum_integer(value);
   constexpr auto index = magic_enum::enum_index(value).value();
   constexpr auto name = magic_enum::enum_name(value);

   std::cout << "Value = " << value << '\n';
   std::cout << "Integer = " << integer << '\n';
   std::cout << "Offset = " << index << '\n';
   std::cout << "Name = " << name << '\n';}<code><pre>


Sortie :

<pre><code>Value = HELLO
Integer = 42
Offset = 0
Name = HELLO<code><pre>

Un monde de trucs cools s'ouvre à nous :

  • Fini les gens qui rajoutent une dernière valeur <span class="css-span">COUNT</span> à l'énumération : à la place, il suffit d'utiliser <span class="css-span">enum_count()</span>.
  • Bonjour les affichages lisibles de valeurs avec "HELLO" à la place de "0" grâce à <span class="css-span">enum_name()</span> ou l'opérateur de stream fourni.
  • A nous la lecture des chaines pour les transformer en valeur de l'union avec <span class="css-span">enum_cast()</span>.
  • Coucou la réponse facile à "cet entier est-il dans mon énumération ?" avec <span class="css-span">enum_cast()</span>.
  • Enfin la possibilité de donner des valeurs numériques arbitraires aux valeurs de l'énumération tout en les utilisant comme index de tableaux grâce a <span class="css-span">enum_index()</span>.

Si votre compilateur est compatible C++17, téléchargez, utilisez, appréciez. Sinon... il est temps de faire du lobbying pour mettre à jour votre compilateur !

No items found.
ça t’a plu ?
Partage ce contenu
Pierre

Que la vie de Pierre, expert embarqué Younup, serait terne sans les variadic templates et les fold expressions de C++17. Heureusement pour lui, Python a tué l'éternel débat sur l’emplacement de l’accolade : "alors, à la fin de la ligne courante ou au début de la ligne suivante ?"

Homme de terrain, il est aussi à l’aise au guidon de son VTT à sillonner les chemins de forêt, dans une salle de concert de black metal ou les mains dans les soudures de sa carte électronique quand il doit déboguer du code (bon ça, il aime moins quand même !)

Son vœu pieux ? Il hésite encore... Faire disparaitre le C embarqué au profit du C++ embarqué ? Ou stopper la génération sans fin d'entropie de son bureau.