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, 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.