DEV Community

Pierre Gradot for Younup

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

4 2

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 😅

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay