Iterate, dispatch, and store enum values
When you need to perform operations across all members of an enumeration or store data associated with specific enum keys, manual loops and standard containers often require error-prone boilerplate or manual size management. magic_enum provides specialized utilities and containers that leverage compile-time metadata to automate iteration, dispatching, and storage.
Compile-time Iteration
If you need to execute logic for every enumerator in a type—such as registering handlers or generating UI elements—magic_enum::enum_for_each applies a callable to every value at compile time.
The callable receives a magic_enum::enum_constant<V> object. To access the actual enum value, you must invoke this object using operator(). This value can then be passed to other magic_enum functions like magic_enum::enum_name.
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Color { Red, Green, Blue };
int main() {
// Iterate over all Color enumerators and print their names.
magic_enum::enum_for_each<Color>([](auto val) {
// val is an enum_constant; invoke it to get the value for enum_name.
std::cout << magic_enum::enum_name(val()) << " ";
});
// Output: Red Green Blue
return 0;
}
Runtime Dispatching
When you have a runtime enum value and need to execute different logic branches, magic_enum::enum_switch provides a functional alternative to a standard switch statement. It maps the runtime value to a compile-time enum_constant within a lambda.
To ensure safety, you should specify an explicit return type (e.g., std::string) as a template argument. If the runtime value is not a valid member of the enum, enum_switch returns a default-constructed instance of that type. The lambda itself must also declare a matching trailing return type.
#include <iostream>
#include <string>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_switch.hpp>
enum class Color { Red, Green, Blue };
int main() {
Color c = Color::Green;
// Dispatch based on runtime value 'c'.
auto result = magic_enum::enum_switch<std::string>([](auto val) -> std::string {
if constexpr (val() == Color::Red) {
return "Stop";
} else if constexpr (val() == Color::Green) {
return "Go";
} else {
return "Caution";
}
}, c);
std::cout << "Action: " << result << std::endl; // Output: Action: Go
return 0;
}
Enum-Indexed Arrays
Storing data for every enum member usually involves a std::array where you manually manage the index mapping. magic_enum::containers::array automates this by allowing you to use enum values directly as keys.
Internally, magic_enum::containers::array wraps a std::array<V, enum_count<E>()>. It uses magic_enum::enum_index to map enum values to their positions in the underlying array. You can access elements using operator[] or the bounds-checked at() method.
#include <cassert>
#include <string>
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { Red, Green, Blue };
int main() {
// Default-construct the array.
magic_enum::containers::array<Color, std::string> color_hex;
// Assign values using enum members as keys.
color_hex[Color::Red] = "#FF0000";
color_hex[Color::Green] = "#00FF00";
color_hex[Color::Blue] = "#0000FF";
assert(color_hex[Color::Red] == "#FF0000");
return 0;
}
Enum Sets
If you need to track a collection of unique enum values, magic_enum::containers::set provides a memory-efficient container with an interface similar to std::set.
Unlike a standard set, this implementation is optimized for enums by using a bitset internally. It provides contains() for membership checks and insert() for adding values.
#include <cassert>
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { Red, Green, Blue };
int main() {
magic_enum::containers::set<Color> palette;
palette.insert(Color::Red);
palette.insert(Color::Blue);
assert(palette.contains(Color::Red));
assert(!palette.contains(Color::Green));
assert(palette.size() == 2);
return 0;
}