Skip to main content

Iterate, dispatch, and store enum values

When you need to perform actions for every member of an enumeration or map enum values to specific data, standard C++ often requires manual maintenance of switch statements or arrays that can easily fall out of sync. magic_enum provides a suite of utilities and containers that automate these patterns, ensuring your logic remains correct even as the enum definition changes.

Iterate over Enum Values

If you need to execute logic for every enumerator in an enum—for example, to populate a UI list or aggregate data—magic_enum::enum_for_each provides a constexpr way to iterate.

Include the magic_enum/magic_enum_utility.hpp header to use this function.

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

enum class Color { Red = 1, Green = 2, Blue = 4 };

void print_colors() {
// Iterates over all values of Color
magic_enum::enum_for_each<Color>([](auto val) {
// val is a magic_enum::enum_constant<Color::Value>
// Use val() to get the actual enum value
std::cout << magic_enum::enum_name(val) << " = "
<< static_cast<int>(val()) << std::endl;
});
}

Internally, magic_enum::enum_for_each uses std::index_sequence to expand the enum values at compile time. The return behavior depends on your lambda:

  • If the lambda returns void, enum_for_each returns void.
  • If the lambda returns the same type for all values, it returns a std::array containing the results.
  • If the lambda returns different types, it returns a std::tuple.

Dispatch with Enum Switch

Traditional switch statements cannot be easily used in generic code or with non-integral types. magic_enum::enum_switch acts as a functional replacement that can be used in constexpr contexts.

Include magic_enum/magic_enum_switch.hpp for this functionality. You must specify an explicit result type (like std::string) to ensure that invalid enum values result in a safe, default-constructed value rather than undefined behavior.

#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) {
// Explicitly specify std::string as the result type
return magic_enum::enum_switch<std::string>([](auto val) -> std::string {
// Every branch must return the specified result type
if constexpr (val == Color::Red) {
return "The color of fire";
} else {
return std::string(magic_enum::enum_name(val));
}
}, c);
}

If the provided enum value is not part of the enumeration, enum_switch returns a default-constructed instance of the result type. You can also provide a custom default value as a third argument:

auto desc = magic_enum::enum_switch<std::string>(
[](auto val) -> std::string { return std::string(magic_enum::enum_name(val)); },
static_cast<Color>(999),
"Unknown Color" // Custom default value
);

Store Data in Enum-Aware Containers

magic_enum provides specialized containers in the magic_enum::containers namespace, found in magic_enum/magic_enum_containers.hpp. These containers are designed to use enums as keys efficiently.

Enum-Indexed Arrays

The magic_enum::containers::array class template wraps std::array but allows you to use enum values directly for indexing. This eliminates the need for manual static_cast to std::size_t.

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

enum class Color { Red, Green, Blue };

void use_array() {
// Define an array mapping Color to int
magic_enum::containers::array<Color, int> color_values;

// Assign values using enum keys
color_values[Color::Red] = 255;
color_values[Color::Green] = 128;
color_values[Color::Blue] = 64;

// Accessing values
int red_val = color_values[Color::Red];

// .at() provides bounds checking against the enum's valid range
int green_val = color_values.at(Color::Green);
}

The container's size is automatically determined by magic_enum::enum_count<E>(). Note that std::array equality is not constexpr in C++17, so when verifying container state in static_assert, you must check individual elements:

constexpr magic_enum::containers::array<Color, int> create_array() {
magic_enum::containers::array<Color, int> a{};
a[Color::Red] = 1;
return a;
}

static_assert(create_array()[Color::Red] == 1, "Red should be 1");

Enum Sets

The magic_enum::containers::set class provides a std::set-like interface for unique enum values. It is implemented using a bitset, making it extremely memory-efficient.

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

enum class Color { Red, Green, Blue };

void use_set() {
magic_enum::containers::set<Color> active_colors;

active_colors.insert(Color::Red);
active_colors.insert(Color::Blue);

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

// Iteration follows the order of enumerators in the enum definition
for (Color c : active_colors) {
// Iterates over Red, then Blue
}
}

The set container supports standard operations like erase, count, and size. It also provides a constructor that accepts an initializer list: magic_enum::containers::set<Color> s = {Color::Red, Color::Green};.