Iterate, dispatch, and store enum values
When you need to perform operations across all members of an enumeration or map enum values to specific data, standard C++ often requires manual switch statements or error-prone manual indexing. magic_enum provides a suite of utilities and containers that 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 apply a callable to every value in an enum. This is useful for aggregation, initialization, or generating parallel data structures.
Basic Iteration
If your callable returns void, enum_for_each simply executes the logic for each value.
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Color { RED, GREEN, BLUE };
void print_all_colors() {
magic_enum::enum_for_each<Color>([](auto val) {
constexpr Color c = val;
std::cout << magic_enum::enum_name(c) << " ";
});
// Output: RED GREEN BLUE
}
Generating Results
If the callable returns a value, enum_for_each collects these results into a container:
std::array: Returned if all invocations return the same type.std::tuple: Returned if invocations return different types (e.g., when using a generic lambda that returns different types based on the enum value).
#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() {
// Returns std::array<std::string_view, 3>
auto names = magic_enum::enum_for_each<Color>([](auto val) {
return magic_enum::enum_name<val>();
});
}
Pattern Matching with Enum Switch
The magic_enum::enum_switch function in magic_enum/magic_enum_switch.hpp provides a functional alternative to the switch statement. It is particularly useful when you need to return a value based on a runtime enum variable while maintaining constexpr compatibility.
Safe Dispatching
When using enum_switch, you should explicitly specify a result type like std::string. This prevents undefined behavior if the enum value is invalid; for example, returning std::string_view from a default case might result in a null pointer conversion, whereas std::string will safely produce an empty string.
#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) {
return magic_enum::enum_switch<std::string>(
[](auto val) -> std::string {
constexpr Color color = val;
if constexpr (color == Color::RED) return "The color of passion";
return "A standard color";
},
c,
"Unknown color" // Optional default value
);
}
Internally, magic_enum::enum_switch uses detail::constexpr_switch to perform a linear search or a hash-based jump (if MAGIC_ENUM_ENABLE_HASH is defined) to find the matching case at compile time.
Enum-Aware Containers
The magic_enum::containers namespace in magic_enum/magic_enum_containers.hpp provides specialized versions of standard containers optimized for enums.
Enum-Indexed Arrays
The magic_enum::containers::array class is a wrapper around std::array that allows you to use enum values directly as indices.
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { RED, GREEN, BLUE };
void use_array() {
magic_enum::containers::array<Color, int> color_values;
// Direct indexing
color_values[Color::RED] = 255;
// Bounds-checked access
try {
color_values.at(Color::GREEN) = 128;
} catch (const std::out_of_range& e) {
// Handle invalid enum value
}
}
The at(E pos) method performs a lookup via index_type::at(pos). If the enum value is not recognized, it calls MAGIC_ENUM_THROW(std::out_of_range(...)).
Compile-Time Sets
The magic_enum::containers::set provides a std::set-like interface but is implemented using a bitset, making it extremely efficient and constexpr compatible.
#include <cassert>
#include <magic_enum/magic_enum_containers.hpp>
enum class Color { RED, GREEN, BLUE };
void use_set() {
constexpr magic_enum::containers::set<Color> my_colors = {Color::RED, Color::BLUE};
static_assert(my_colors.contains(Color::RED));
static_assert(!my_colors.contains(Color::GREEN));
auto color_set = magic_enum::containers::set<Color>();
color_set.insert(Color::GREEN);
assert(color_set.size() == 1);
}
The set uses detail::indexing<E, Cmp> to map enum values to bit positions. By default, it uses std::less<E> for ordering, but you can provide custom comparators like magic_enum::containers::name_less<Color> to sort the set elements by their string names instead of their underlying integer values.