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 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.hpp alone for these features. Use magic_enum/magic_enum_utility.hpp for iteration, magic_enum/magic_enum_switch.hpp for dispatching, and magic_enum/magic_enum_containers.hpp for containers.
  • Lambda Parameters: In enum_for_each and enum_switch, the lambda parameter val is an object of type magic_enum::enum_constant. You cannot pass val directly to enum_name. You must use enum_name(val()) or enum_name<val()>().
  • C++17 Constexpr Limits: In C++17, std::array::operator== is not constexpr. If you need to verify magic_enum::containers::array contents in a static_assert, you must check individual elements:
    static_assert(palette[RGB::Red] == 0xFF0000); // Valid
    // static_assert(palette == other_palette); // Invalid in C++17