Skip to main content

Iterate, dispatch, and store enum values

When you need to perform operations across all members of an enumeration or store data associated with specific enum keys, standard C++ requires manual maintenance of arrays and switch statements. magic_enum provides utilities to automate these patterns, ensuring that your logic stays in sync with your enum definitions.

Iterating Over Enum Values

The magic_enum::enum_for_each function in magic_enum/magic_enum_utility.hpp allows you to execute a callable for every value in an enum at compile time. This is useful for generating reports, initializing data structures, or performing batch operations.

Basic Iteration

To perform a side-effect for each enum value, pass a lambda that accepts a magic_enum::enum_constant<V>.

#include <iostream>
#include <string>
#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
}

Collecting Results

If the callable returns a value, enum_for_each collects these into a container. If all return types are identical, it returns a std::array. If they differ, it returns a std::tuple.

#include <array>
#include <string_view>
#include "magic_enum/magic_enum.hpp"
#include "magic_enum/magic_enum_utility.hpp"

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

Type-Safe Dispatching

The magic_enum::enum_switch function in magic_enum/magic_enum_switch.hpp provides a functional alternative to the switch statement. It maps a runtime enum value to a compile-time constant within a visitor.

Handling Specific Cases

You can use a visitor (like a lambda or an overloaded helper) to handle specific values. It is highly recommended to specify an explicit result type to ensure safety.

#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_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 fire.";
} else {
return std::string(magic_enum::enum_name(color));
}
}, c);
}

Safety and Default Values

If enum_switch is called with an invalid enum value (e.g., a casted integer that isn't a member), it returns a default-constructed instance of the result type. By providing a third argument, you can specify a custom default value.

#include <cassert>
#include <string>
#include "magic_enum/magic_enum.hpp"
#include "magic_enum/magic_enum_switch.hpp"

void check_switch(Color c) {
// Returns empty string if c is invalid
auto res = magic_enum::enum_switch<std::string>([](auto val) {
return std::string(magic_enum::enum_name<val>());
}, c);

// Returns "Unknown" if c is invalid
auto res_with_default = magic_enum::enum_switch<std::string>([](auto val) {
return std::string(magic_enum::enum_name<val>());
}, c, "Unknown");
}

Enum-Aware Containers

The magic_enum/magic_enum_containers.hpp header provides containers that use enums as primary keys, offering better type safety and performance than std::map for enum-indexed data.

Enum-Indexed Arrays

magic_enum::containers::array<E, V> is a wrapper around std::array<V, enum_count<E>()>. It allows you to use enum values directly for indexing.

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

enum class Color { Red, Green, Blue };

void use_array() {
magic_enum::containers::array<Color, int> scores{{10, 20, 30}};

// Access via enum value
scores[Color::Green] = 25;

// Bounds-checked access throws std::out_of_range for invalid enums
try {
scores.at(static_cast<Color>(99)) = 0;
} catch (const std::out_of_range& e) {
std::cerr << e.what() << std::endl;
}

// Compile-time access via get
int red_score = magic_enum::containers::get<Color::Red>(scores);
}

Enum Sets

magic_enum::containers::set<E> provides a set-like interface for enums, internally optimized using a bitset. It supports standard operations like insert, erase, and contains.

#include <iostream>
#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 {Color::Red, Color::Blue};

if (active_colors.contains(Color::Red)) {
std::cout << "Red is active" << std::endl;
}

active_colors.insert(Color::Green);
active_colors.erase(Color::Blue);

std::cout << "Set size: " << active_colors.size() << std::endl; // Prints 2
}

Internally, magic_enum::containers::set uses magic_enum::containers::bitset to store presence. This makes operations like contains extremely fast (O(1)) compared to std::set (O(log N)).