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.