Iterate, dispatch, and store enum values
When you need to perform actions for every member of an enumeration or dispatch logic based on a runtime enum value, standard C++ often requires repetitive switch blocks or manual array management. magic_enum provides a set of utilities and containers to automate these patterns safely and efficiently.
Iterating Over Enum Values
If you need to execute a function for every value in an enum—for example, to initialize a registry or log all available options—you can use magic_enum::enum_for_each. This function accepts a callable and applies it to every enumerator.
Side Effects and Void Returns
To perform an action without returning a value, pass a callable that returns void. The callable receives a magic_enum::enum_constant<V> object, which can be used to retrieve the enum value at compile-time.
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Color { RED, GREEN, BLUE };
void log_colors() {
magic_enum::enum_for_each<Color>([](auto val) {
// val is magic_enum::enum_constant<Color::VALUE>
constexpr Color c = val;
std::cout << "Color: " << magic_enum::enum_name(c) << std::endl;
});
}
Transforming Enums into Arrays
If the callable returns a value, magic_enum::enum_for_each collects these results into a std::array. This is useful for generating lookup tables or metadata associated with each enum member.
#include <array>
#include <string_view>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Color { RED, GREEN, BLUE };
void example() {
constexpr auto names = magic_enum::enum_for_each<Color>([](auto val) {
return magic_enum::enum_name<val()>();
});
// names is std::array<std::string_view, 3>{"RED", "GREEN", "BLUE"}
}
Dispatching with Enum Switch
The magic_enum::enum_switch function provides a functional alternative to the switch statement. It is particularly useful when you need to map a runtime enum value to a compile-time context (like a template parameter).
Safe Dispatching
When using enum_switch, you should specify an explicit result type. If an invalid or unrecognized enum value is passed, the function returns a default-constructed instance of that type, preventing undefined behavior or crashes from invalid conversions.
#include <iostream>
#include <string>
#include <magic_enum/magic_enum_switch.hpp>
enum class Color { RED, GREEN, BLUE };
// Helper for visitor pattern
template <typename... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <typename... Ts> overloaded(Ts...) -> overloaded<Ts...>;
void dispatch(Color c) {
auto handler = overloaded{
[](magic_enum::enum_constant<Color::RED>) { return std::string{"Handling Red"}; },
[](magic_enum::enum_constant<Color::GREEN>) { return std::string{"Handling Green"}; },
[](Color other) { return "Default: " + std::string{magic_enum::enum_name(other)}; }
};
// Explicitly specify <std::string> for safety
std::string result = magic_enum::enum_switch<std::string>(handler, c);
std::cout << result << std::endl;
}
If you pass an invalid enum value (e.g., static_cast<Color>(99)) and no default case is provided in the callable, enum_switch returns an empty std::string. You can also provide a fallback value as the third argument:
// Returns "Unknown" if c is invalid
std::string result = magic_enum::enum_switch<std::string>(handler, c, "Unknown");
Enum-Aware Containers
magic_enum provides specialized containers in magic_enum/magic_enum_containers.hpp that use enum values as keys or indices.
Enum-Indexed Arrays
The magic_enum::containers::array class is a wrapper around std::array. It allows you to access elements directly using enum values, providing a type-safe alternative to manual integer casting.
#include <iostream>
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { RED, GREEN, BLUE };
void use_array() {
// Initialize with values corresponding to RED, GREEN, BLUE
magic_enum::containers::array<Color, int> scores{{10, 20, 30}};
// Access by enum value
scores[Color::GREEN] = 25;
// Access via magic_enum::containers::get for compile-time safety
int red_score = magic_enum::containers::get<Color::RED>(scores);
}
Internally, magic_enum::containers::array uses an Index strategy to map enum values to the underlying std::array indices. It supports standard container methods like at(), begin(), end(), and size().
Efficient Enum Sets
The magic_enum::containers::set provides a set-like interface for enums. It is implemented using a bitset, making it significantly more memory-efficient and faster than std::set<E> for enums with a small range of values.
#include <iostream>
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { RED, GREEN, BLUE };
void use_set() {
magic_enum::containers::set<Color> active_colors {Color::RED, Color::BLUE};
if (active_colors.contains(Color::RED)) {
std::cout << "Red is active" << std::endl;
}
active_colors.insert(Color::GREEN);
active_colors.erase(Color::BLUE);
std::cout << "Total active: " << active_colors.size() << std::endl;
}
Because it uses a bitset, magic_enum::containers::set is ideal for flags or tracking state across a known set of enumerators. It provides methods like insert, erase, clear, and contains (or count) that mirror the std::set API.