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 structures, standard C++ often requires manual maintenance of arrays or repetitive switch statements. magic_enum provides utilities to automate these patterns, ensuring that your logic remains synchronized with your enum definitions.
Iterating Over Enum Values
The enum_for_each function allows you to execute a callable for every value in an enumeration. This is useful for generating reports, initializing registries, or performing batch operations.
Basic Iteration
To use enum_for_each, include the magic_enum/magic_enum_utility.hpp header. The function passes a magic_enum::enum_constant wrapper to your lambda. You must invoke this wrapper with () to retrieve the actual enum value.
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Color { Red, Green, Blue };
void print_colors() {
// Iterates over Red, Green, Blue
magic_enum::enum_for_each<Color>([](auto val) {
// val is magic_enum::enum_constant<Color::Value>
// Use val() to get the enum value for magic_enum functions
std::cout << magic_enum::enum_name(val()) << " ";
});
}
Collecting Results
If the lambda passed to enum_for_each returns a value, the function returns a std::array containing the results of each invocation.
#include <string>
#include <vector>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_utility.hpp>
enum class Direction { Up, Down, Left, Right };
auto get_direction_names() {
// Returns std::array<std::string_view, 4>
return magic_enum::enum_for_each<Direction>([](auto val) {
return magic_enum::enum_name(val());
});
}
Type-Safe Dispatching
The 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 an enum member known only at runtime.
Safe Runtime Dispatch
When using enum_switch, you should explicitly specify the result type. If the provided enum value is invalid or not handled, enum_switch returns a default-constructed instance of that type, preventing undefined behavior or null pointer dereferences.
#include <string>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_switch.hpp>
enum class Status { Active, Suspended, Deleted };
std::string get_status_description(Status s) {
// Explicitly specify <std::string> as the result type.
// The lambda must declare a matching trailing return type.
return magic_enum::enum_switch<std::string>(
[](auto val) -> std::string {
constexpr Status s_val = val();
if constexpr (s_val == Status::Active) {
return "The account is currently active.";
} else {
return std::string(magic_enum::enum_name(s_val)) + " status.";
}
},
s
);
}
Enum-Aware Containers
The magic_enum::containers namespace provides specialized versions of standard containers that use enum values as keys. These are available in magic_enum/magic_enum_containers.hpp.
Associative Arrays
The magic_enum::containers::array class is a wrapper around std::array. It allows you to use enum values directly as indices, providing a more readable alternative to manual integer indexing.
The recommended pattern is to default-construct the array and then assign values using the enum keys.
#include <iostream>
#include <magic_enum/magic_enum_containers.hpp>
enum class RGB { Red, Green, Blue };
void configure_palette() {
// Default-construct the container
magic_enum::containers::array<RGB, int> palette;
// Assign values using enum keys
palette[RGB::Red] = 0xFF0000;
palette[RGB::Green] = 0x00FF00;
palette[RGB::Blue] = 0x0000FF;
// Accessing values
std::cout << "Red value: " << palette.at(RGB::Red) << std::endl;
}
Internally, magic_enum::containers::array uses magic_enum::enum_index to map the enum value to the underlying std::array index. Note that at() will throw std::out_of_range if an invalid enum value is provided, while operator[] uses assertions in debug builds.
Unique Sets
The magic_enum::containers::set class provides a bitset-backed container for storing unique enum values. It offers an interface similar to std::set but with significantly better performance and a smaller memory footprint for enums.
#include <cassert>
#include <magic_enum/magic_enum_containers.hpp>
enum class Permission { Read, Write, Execute };
void manage_permissions() {
magic_enum::containers::set<Permission> permissions;
permissions.insert(Permission::Read);
permissions.insert(Permission::Write);
// Check for existence
if (permissions.contains(Permission::Read)) {
// ...
}
assert(permissions.size() == 2);
}
Implementation Details and Constraints
- Header Requirements: Do not include
magic_enum.hppalone for these features. Usemagic_enum/magic_enum_utility.hppfor iteration,magic_enum/magic_enum_switch.hppfor dispatching, andmagic_enum/magic_enum_containers.hppfor containers. - Lambda Parameters: In
enum_for_eachandenum_switch, the lambda parametervalis an object of typemagic_enum::enum_constant. You cannot passvaldirectly toenum_name. You must useenum_name(val())orenum_name<val()>(). - C++17 Constexpr Limits: In C++17,
std::array::operator==is notconstexpr. If you need to verifymagic_enum::containers::arraycontents in astatic_assert, you must check individual elements:static_assert(palette[RGB::Red] == 0xFF0000); // Valid
// static_assert(palette == other_palette); // Invalid in C++17