DEV Community

Pierre Gradot for Younup

Posted on • Edited on • Originally published at younup.fr

Magic Enum : la révolution des énumérations en C++

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 :

#include <iostream>
#include "magic_enum.hpp"

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

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

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

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

    std::cout << '\n';

    for (unsigned int i = 0; i < count; ++i) {
        auto value = magic_enum::enum_value<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<MyEnum>(0);
    constexpr  auto HELLO = magic_enum::enum_cast<MyEnum>("HELLO");
    constexpr auto hello = magic_enum::enum_cast<MyEnum>("hello");
    constexpr auto WORLD = magic_enum::enum_cast<MyEnum>("WORLD");
    constexpr auto WORLD_AGAIN = magic_enum::enum_cast<MyEnum>(99);

    std::cout << invalid << '\n';
    std::cout << HELLO << '\n';
    std::cout << hello << '\n';
    std::cout << WORLD << '\n';
}
Enter fullscreen mode Exit fullscreen mode

Sortie :

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
Enter fullscreen mode Exit fullscreen mode

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 :

int main() {
    using namespace magic_enum::ostream_operators;

    constexpr auto value = magic_enum::enum_cast<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';
}
Enter fullscreen mode Exit fullscreen mode

Sortie :

Value = HELLO
Integer = 42
Offset = 0
Name = HELLO
Enter fullscreen mode Exit fullscreen mode

Un monde de trucs cools s'ouvre à nous :

  • Fini les gens qui rajoutent une dernière valeur COUNT à l'énumération : à la place, il suffit d'utiliser enum_count().
  • Bonjour les affichages lisibles de valeurs avec "HELLO" à la place de "0" grâce à enum_name() ou l'opérateur de stream fourni.
  • A nous la lecture des chaines pour les transformer en valeur de l'union avec enum_cast().
  • Coucou la réponse facile à "cet entier est-il dans mon énumération ?" avec enum_cast().
  • 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 enum_index().

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 😅

Top comments (0)