Skip to main content

Iterate, dispatch, and store enum values

When you need to perform operations across all enumerators or map enum values to specific logic and storage, standard C++ requires manual switch statements or boilerplate-heavy arrays. magic_enum provides utilities to automate these patterns at compile time, ensuring that your logic stays in sync with your enum definitions.

Iterating Over Enums

The magic_enum::enum_for_each function, defined in magic_enum/magic_enum_utility.hpp, allows you to execute a lambda for every value in an enum. This is useful for tasks like generating UI elements, serializing all possible states, or performing aggregate calculations.

The lambda you provide receives an enum_constant<V> object. To access the actual enum value, you must call the object: val(). To get the name of the enumerator, use magic_enum::enum_name(val()).

#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>

enum class Color { Red = 1, Green = 2, Blue = 4 };

void print_all_colors() {
// Iterates over Red, Green, Blue
magic_enum::enum_for_each<Color>([](auto val) {
constexpr Color c = val();
std::cout << magic_enum::enum_name(c) << " = " << static_cast<int>(c) << std::endl;
});
}

If your lambda returns a value, enum_for_each collects these results. If all return types are the same, it returns a std::array; otherwise, it returns a std::tuple.

Dispatching with Enum Switch

When you have a runtime enum value and need to call a specific handler, magic_enum::enum_switch (in magic_enum/magic_enum_switch.hpp) provides a compile-time generated dispatch mechanism. This is safer and more expressive than a manual switch block.

You should always specify an explicit result type (like std::string) to ensure that invalid enum values result in a safe default value rather than undefined behavior (such as a null std::string_view).

#include <iostream>
#include <string>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_switch.hpp>

enum class Color { Red, Green, Blue };

std::string get_description(Color c) {
return magic_enum::enum_switch<std::string>(
[](auto val) {
constexpr Color color = val();
if constexpr (color == Color::Red) return "The color of passion";
return std::string(magic_enum::enum_name(color));
},
c,
"Unknown Color" // Default value if 'c' is invalid
);
}

Internally, magic_enum implements this using a recursive constexpr_switch_impl. If you define MAGIC_ENUM_ENABLE_HASH, the library uses a hash-based lookup which can improve performance for enums with many values.

Enum-Aware Storage

The magic_enum::containers namespace in magic_enum/magic_enum_containers.hpp provides specialized versions of standard containers that use enums as keys.

Enum-Indexed Arrays

The magic_enum::containers::array class is a wrapper around std::array where the index is the enum itself. It ensures the array size exactly matches the number of enumerators.

#include <magic_enum/magic_enum_containers.hpp>

enum class Color { Red, Green, Blue };

void store_rgb_values() {
struct RGB { int r, g, b; };

// Creates an array of 3 RGB structs
magic_enum::containers::array<Color, RGB> color_data;

color_data[Color::Red] = {255, 0, 0};

// .at() throws std::out_of_range if the enum value is invalid
auto green = color_data.at(Color::Green);
}

Warning: While you can access the underlying data via iterators, sorting the data (e.g., using std::sort) will break the mapping between the enum values and their expected indices.

Efficient Enum Sets

The magic_enum::containers::set class provides a std::set-like interface but is optimized for enums using a bitset internally. It supports standard operations like insert, erase, and contains.

#include <magic_enum/magic_enum_containers.hpp>

enum class Color { Red, Green, Blue };

void manage_palette() {
magic_enum::containers::set<Color> palette;

palette.insert(Color::Red);
palette.insert(Color::Blue);

if (palette.contains(Color::Red)) {
// ...
}

palette.erase(Color::Red);
}

The set implementation uses a FilteredIterator to skip over bits that are not set, allowing you to iterate only over the enum values currently present in the container. By default, it uses the enum's numeric value for ordering, but you can provide a custom comparator like magic_enum::containers::name_less<Color> to order by enumerator name.