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++ often requires manual maintenance of arrays or switch statements. magic_enum provides a suite of utilities and containers that automate these tasks by leveraging compile-time reflection.

Iterating with enum_for_each

The magic_enum::enum_for_each function allows you to execute a callable for every value in an enum. This is useful for generating reports, initializing resources, or aggregating data across all enum members.

The callable you pass to enum_for_each receives an enum_constant<V> wrapper. To access the actual enum value, you must invoke the wrapper (e.g., val()). This value can then be passed to other functions like enum_name.

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

enum class Color { RED, GREEN, BLUE };

int main() {
// Iterate over all Color values and print their names.
// The lambda parameter 'val' is an enum_constant wrapper.
magic_enum::enum_for_each<Color>([](auto val) {
// val() must be invoked to get the enum value for enum_name.
std::cout << magic_enum::enum_name(val()) << " ";
});
// Output: RED GREEN BLUE
return 0;
}

Dispatching with enum_switch

When you need to map a runtime enum value to a specific result or action, magic_enum::enum_switch provides a compile-time generated dispatch mechanism. It acts as a functional switch statement that can return a value.

To ensure safety, you should explicitly specify the return type as a template argument (e.g., magic_enum::enum_switch<std::string>). If the provided enum value is invalid (e.g., a cast from an out-of-range integer), enum_switch will return a default-constructed instance of that type instead of triggering 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 };

int main() {
Color c = Color::GREEN;

// Dispatch based on the value of 'c'.
// The lambda must declare a trailing return type matching the switch result.
auto color_name = magic_enum::enum_switch<std::string>([](auto val) -> std::string {
return std::string{magic_enum::enum_name(val())};
}, c);

std::cout << "Selected: " << color_name << std::endl; // Selected: GREEN
return 0;
}

Storing Data with containers::array

The magic_enum::containers::array class is a wrapper around std::array that allows you to use enum values as indices. This eliminates the need for manual integer casting and ensures the array size always matches the number of enum members.

In magic_enum, the recommended pattern is to default-construct the array and then assign values to specific enum keys using operator[].

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

enum class Color { RED, GREEN, BLUE };

int main() {
// Create an array mapping Color to std::string.
magic_enum::containers::array<Color, std::string> color_hints;

// Assign values using enum keys.
color_hints[Color::RED] = "Stop";
color_hints[Color::GREEN] = "Go";
color_hints[Color::BLUE] = "Caution";

// Access values safely.
std::cout << "RED means: " << color_hints[Color::RED] << std::endl;
return 0;
}

Managing Collections with containers::set

The magic_enum::containers::set class provides a set-like interface for enum values, implemented efficiently using a bitset. It supports standard set operations like insert, erase, and contains.

Because it implements the standard container requirements, you can iterate over a containers::set using a range-based for loop, which will yield the enum values currently present in the set.

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

enum class Color { RED, GREEN, BLUE };

int main() {
magic_enum::containers::set<Color> active_colors;

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

// Check for presence.
if (active_colors.contains(Color::RED)) {
std::cout << "RED is active." << std::endl;
}

// Iterate over the set.
for (auto c : active_colors) {
std::cout << "Active: " << magic_enum::enum_name(c) << std::endl;
}

return 0;
}