Skip to main content

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, manual switch statements and raw arrays are error-prone and difficult to maintain. magic_enum provides a suite of functional utilities and specialized containers that leverage compile-time reflection to automate iteration, dispatching, and storage.

Compile-Time Iteration

If you need to execute logic for every value in an enum—such as populating a UI list or calculating a checksum—magic_enum::enum_for_each allows you to iterate over all reflected values at compile time.

Basic Iteration

You can pass a callable (like a lambda) to enum_for_each. The callable receives a magic_enum::enum_constant<V> representing each enum value.

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

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

void print_all_colors() {
magic_enum::enum_for_each<Color>([](auto val) {
constexpr Color c = val;
std::cout << magic_enum::enum_name(c) << " = " << magic_enum::enum_integer(c) << std::endl;
});
}

Collecting Results

magic_enum::enum_for_each is not just for side effects. If your callable returns a value, enum_for_each collects these into a container:

  • If the callable returns void, enum_for_each returns void.
  • If the callable returns the same type T for all values, it returns a std::array<T, N>.
  • If the callable returns different types, it returns a std::tuple.

This is useful for generating lookup tables or metadata arrays:

auto color_names = magic_enum::enum_for_each<Color>([](auto val) {
return magic_enum::enum_name(val.value);
});
// color_names is a std::array<std::string_view, 3>

Functional Dispatching

Replacing a switch statement with magic_enum::enum_switch allows for more expressive dispatching, especially when you need to return values or handle default cases uniformly.

Dispatching to Callables

magic_enum::enum_switch takes a callable and a runtime enum value. It executes the branch of the callable that matches the enum value.

#include <magic_enum/magic_enum_switch.hpp>

std::string_view get_description(Color c) {
return magic_enum::enum_switch([](auto val) {
constexpr Color color = val;
if constexpr (color == Color::RED) return "Stop";
else if constexpr (color == Color::GREEN) return "Go";
else return "Unknown";
}, c);
}

Handling Invalid Values and Defaults

If the runtime value does not match any reflected enum member, enum_switch returns a default-constructed value of the result type. You can override this by providing an explicit default value as the third argument:

// Returns "Invalid" if c is not a valid Color member
auto result = magic_enum::enum_switch<std::string>(
[](auto val) { return std::string(magic_enum::enum_name(val)); },
c,
"Invalid"
);

Internally, enum_switch implements a constexpr_switch_impl (found in include/magic_enum/magic_enum_switch.hpp) that performs a linear search over values by default. For enums with many values, you can define MAGIC_ENUM_ENABLE_HASH_SWITCH to enable a hash-based dispatch mechanism.

Enum-Aware Containers

Standard containers like std::array and std::set require manual indexing or integer casting when used with enums. magic_enum::containers provides specialized versions that understand enum types natively.

Enum-Indexed Arrays

magic_enum::containers::array<E, V> wraps a std::array but allows indexing directly with enum values.

#include <magic_enum/magic_enum_containers.hpp>

magic_enum::containers::array<Color, std::string> color_hints;
color_hints[Color::RED] = "Danger";
color_hints[Color::GREEN] = "Safe";

// Safe access with bounds checking
try {
auto& hint = color_hints.at(static_cast<Color>(99));
} catch (const std::out_of_range& e) {
// Handle invalid enum index
}

The array class (in include/magic_enum/magic_enum_containers.hpp) uses a default_indexing<E> strategy to map enum values to contiguous array indices. If your enum has large gaps or custom ordering requirements, you can provide a custom Index template parameter.

Efficient Enum Sets

magic_enum::containers::set<E> provides a std::set-like interface but is implemented using a bitset for high performance and low memory overhead.

magic_enum::containers::set<Color> active_colors;
active_colors.insert(Color::RED);

if (active_colors.contains(Color::RED)) {
// ...
}

Custom Sorting and String Lookup

By using magic_enum::containers::name_less, you can create a set that is sorted alphabetically by the enum names rather than their underlying integer values. This also enables transparent lookup using strings:

using name_set = magic_enum::containers::set<Color, magic_enum::containers::name_less<>>;
name_set colors = {Color::RED, Color::GREEN};

// Transparent lookup via string_view
if (colors.contains("RED")) {
// Found Color::RED
}

The set implementation relies on detail::FilteredIterator to skip bits that are not set, ensuring that iteration only visits members actually present in the set. Memory usage for magic_enum::containers::set is proportional to the number of reflected enum values, making it extremely efficient for enums within the standard reflection range.