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 😅

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs