Skip to main content

Iterate, dispatch, and store enum values

When you need to perform operations across all enumerators or map enum values to specific data, manual switch statements and standard containers often lead to boilerplate and runtime overhead. magic_enum provides specialized utilities to iterate, dispatch, and store enum values with compile-time efficiency and type safety.

Iterating Over Enum Values

The magic_enum::enum_for_each function allows you to execute a function for every value in an enum. This is useful for tasks like calculating sums or generating metadata for all enumerators.

Basic Iteration

If your lambda returns void, enum_for_each simply executes the logic for each value.

#include <magic_enum.hpp>
#include <iostream>

enum class Color { RED = 1, GREEN = 2, BLUE = 4 };

void print_all_colors() {
int sum = 0;
magic_enum::enum_for_each<Color>([&sum](auto val) {
// val is a magic_enum::enum_constant<Color::VALUE>
sum += static_cast<int>(val);
});
// sum is 7
}

Collecting Results

If the lambda returns a value, enum_for_each collects these results into a std::array.

#include <magic_enum.hpp>
#include <array>

constexpr auto color_flags = magic_enum::enum_for_each<Color>([](auto val) {
return static_cast<int>(val) > 1;
});
// color_flags is std::array<bool, 3> {false, true, true}

Dispatching with Enum Switch

Traditional switch statements cannot return values directly and often require a default case to satisfy compiler warnings. magic_enum::enum_switch provides a functional alternative that can return values and handle invalid enum values safely.

Safe String Dispatching

When returning std::string_view from a dispatch function, you risk returning a view to a temporary if the enum value is invalid. To prevent this, specify std::string as the explicit result type.

#include <magic_enum/magic_enum_switch.hpp>
#include <string>
#include <string_view>

std::string get_color_description(Color c) {
// Explicitly specify std::string as the Result type to ensure safety
return magic_enum::enum_switch<std::string>([](auto val) {
constexpr Color c_val = val;
if constexpr (c_val == Color::RED) {
return "The color of fire";
} else {
return "A cool color";
}
}, c, std::string{"Unknown color"}); // Provide a default value
}

Internally, enum_switch uses detail::constexpr_switch to perform a compile-time search for the matching enumerator. If no match is found, it invokes the provided default value or a default-constructed result.

Storing Data in Enum-Aware Containers

magic_enum provides containers in the magic_enum::containers namespace that are optimized for enum keys.

Enum-Indexed Arrays

The magic_enum::containers::array class is a wrapper around std::array that uses an enum as its index. It ensures that the array size exactly matches the number of reflected enumerators.

#include <magic_enum/magic_enum_containers.hpp>
#include <string>

void store_color_names() {
magic_enum::containers::array<Color, std::string> names;

// Unchecked access using operator[]
names[Color::RED] = "Red";

// Bounds-checked access using at()
// Throws std::out_of_range if the enum value is not reflected
names.at(Color::GREEN) = "Green";
}

The array implementation in include/magic_enum/magic_enum_containers.hpp uses a detail::indexing strategy to map enum values to array offsets. By default, it uses enum_index(pos) to resolve the position.

Efficient Enum Sets

For storing a collection of unique enum values, magic_enum::containers::set offers a memory-efficient implementation based on a bitset.

#include <magic_enum/magic_enum_containers.hpp>

void manage_color_selection() {
magic_enum::containers::set<Color> selected_colors;

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

if (selected_colors.contains(Color::RED)) {
// Logic for red
}

// Iteration only visits inserted values
for (Color c : selected_colors) {
// ...
}
}

The set class uses a bitset internally, which calculates the required number of bits based on enum_count<E>(). This makes it significantly faster and smaller than std::set<E> for small enums.

Configuration and Constraints

The behavior of these utilities depends on the reflected range of the enum. If an enum value falls outside the range defined by MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX (defaulting to -128 to 127), it will not be visited by enum_for_each and will cause array::at() to throw.

You can specialize these ranges for specific enums to include larger values:

template <>
struct magic_enum::customize::enum_range<Color> {
static constexpr int min = 0;
static constexpr int max = 1024;
};