Skip to main content

Iterate, dispatch, and store enum values

When you need to perform operations on every member of an enumeration or map enum values to specific logic at runtime, standard C++ often requires manual maintenance of switch statements or arrays. magic_enum provides utilities to automate these patterns safely and efficiently.

Compile-Time Iteration

If you need to execute logic for every value in an enum—for example, to populate a UI list or calculate a checksum—magic_enum::enum_for_each allows you to iterate over all reflected enumerators at compile time.

Include the magic_enum/magic_enum_utility.hpp header to use this function. The lambda you provide receives a magic_enum::enum_constant<V> object. To access the actual enum value, you must invoke this object as val().

#include <iostream>
#include <string>
#include <vector>
#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() {
std::vector<std::string> names;

// Iterates over RED, GREEN, BLUE
magic_enum::enum_for_each<Color>([&names](auto val) {
// val is an enum_constant object; call it to get the value
auto name = magic_enum::enum_name(val());
names.emplace_back(name);
});

for (const auto& name : names) {
std::cout << name << " ";
}
}

Internally, magic_enum::enum_for_each uses std::index_sequence to expand the enum values into a series of function calls. If your lambda returns a value instead of void, enum_for_each will collect those results into a std::array (if all return types are the same) or a std::tuple.

Type-Safe Dispatching

When you have a runtime enum value and need to call a template function or access a constexpr property based on that value, a standard switch is insufficient because it cannot bridge the gap between runtime values and compile-time constants. magic_enum::enum_switch solves this by generating the necessary dispatch logic.

Include magic_enum/magic_enum_switch.hpp for this functionality. You must specify an explicit result type for the switch to ensure safety; if the runtime value is not a valid enum member, the switch returns a default-constructed instance of that type.

#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_color_description(Color c) {
// Explicitly specify std::string as the result type
return magic_enum::enum_switch<std::string>([](auto val) -> std::string {
// val is a compile-time enum_constant
constexpr Color color = val();
if constexpr (color == Color::RED) {
return "The color of passion";
} else {
return std::string(magic_enum::enum_name<color>());
}
}, c);
}

In this example, the lambda must declare a trailing return type -> std::string to match the Result template parameter. If c is an invalid value (e.g., static_cast<Color>(99)), enum_switch returns an empty std::string.

Enum-Aware Containers

Storing data associated with enums is common, but std::array requires manual indexing, and std::map introduces runtime overhead. magic_enum provides specialized containers in magic_enum/magic_enum_containers.hpp.

Array Storage

magic_enum::containers::array is a wrapper around std::array that uses enum values as keys. It automatically sizes itself to the number of enumerators.

#include <cassert>
#include "magic_enum/magic_enum.hpp"
#include "magic_enum/magic_enum_containers.hpp"

enum class Color { RED, GREEN, BLUE };

void store_rgb_values() {
// Default-construct the container
magic_enum::containers::array<Color, int> color_values;

// Assign entries by enum key
color_values[Color::RED] = 255;
color_values[Color::GREEN] = 128;
color_values[Color::BLUE] = 0;

// Access is type-safe
assert(color_values[Color::RED] == 255);

// at() provides bounds checking and throws std::out_of_range for invalid enums
// color_values.at(static_cast<Color>(10)) -> throws
}

Bitset-Based Sets

magic_enum::containers::set provides a std::set-like interface but is implemented using a bitset for extreme efficiency. It is ideal for tracking a collection of enum flags or unique selections.

#include <cassert>
#include "magic_enum/magic_enum.hpp"
#include "magic_enum/magic_enum_containers.hpp"

enum class Color { RED, GREEN, BLUE };

void manage_color_set() {
magic_enum::containers::set<Color> active_colors;

active_colors.insert(Color::RED);
active_colors.insert(Color::BLUE);

assert(active_colors.contains(Color::RED));
assert(!active_colors.contains(Color::GREEN));
assert(active_colors.size() == 2);

active_colors.erase(Color::RED);
assert(active_colors.size() == 1);
}

Because magic_enum::containers::set uses a bitset internally, operations like insert, erase, and contains are typically constant-time bitwise operations. The iteration order of the set follows the order of the enum values as defined in the source code.