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 loops and arrays. magic_enum provides specialized utilities and containers that automate these tasks by leveraging compile-time enum metadata.

Iterating with enum_for_each

If you need to execute a function for every value in an enum—for example, to aggregate data or generate a report—magic_enum::enum_for_each provides a type-safe way to iterate without manually managing loops or checking bounds.

The function accepts a callable that takes a magic_enum::enum_constant parameter. You must invoke this parameter (e.g., val()) to retrieve the actual enum value before passing it to other functions like magic_enum::enum_name.

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

enum class Color { RED = 1, GREEN = 2, BLUE = 4 };

int main() {
int total_value = 0;

// Iterate over all enum values and sum their underlying integers.
magic_enum::enum_for_each<Color>([&total_value](auto val) {
// val is a magic_enum::enum_constant; invoke it to get the enum value.
constexpr Color c = val();
std::cout << "Processing: " << magic_enum::enum_name(c) << std::endl;
total_value += static_cast<int>(c);
});

assert(total_value == 7);
return 0;
}

Dispatching with enum_switch

When you have a runtime enum value and need to execute logic that depends on that value, magic_enum::enum_switch acts as a functional switch statement. Unlike a standard switch, it can return a value directly and ensures that every case is handled via a generic lambda.

To ensure safety, you must specify an explicit result type (e.g., std::string). This prevents issues like returning a std::string_view that might point to invalid memory if an unrecognized enum value is encountered. The lambda must also declare a matching trailing return type.

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

enum class Status { OK, ERROR, PENDING };

int main() {
Status current_status = Status::ERROR;

// Dispatch based on the runtime value of current_status.
std::string message = magic_enum::enum_switch<std::string>(
[](auto val) -> std::string {
constexpr Status s = val();
if constexpr (s == Status::OK) {
return "Operation successful";
} else if constexpr (s == Status::ERROR) {
return "An error occurred: " + std::string{magic_enum::enum_name(s)};
} else {
return "Status is " + std::string{magic_enum::enum_name(s)};
}
},
current_status
);

std::cout << message << std::endl;
return 0;
}

Storing Data with containers::array

Mapping data to enum values usually involves a std::array and manual casting of enum values to integers. magic_enum::containers::array simplifies this by allowing you to use the enum type directly as an index.

Internally, magic_enum::containers::array wraps a std::array<V, enum_count<E>()>. It provides at() for checked access (throwing std::out_of_range on failure) and operator[] for unchecked access.

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

enum class Tool { HAMMER, WRENCH, DRILL };

int main() {
// Default-construct the container.
magic_enum::containers::array<Tool, int> tool_counts;

// Assign values using enum keys.
tool_counts[Tool::HAMMER] = 5;
tool_counts[Tool::WRENCH] = 12;
tool_counts[Tool::DRILL] = 3;

// Access values safely.
assert(tool_counts.at(Tool::WRENCH) == 12);
assert(tool_counts.size() == 3);

return 0;
}

Managing Collections with containers::set

If you need to track a collection of unique enum values, magic_enum::containers::set provides a memory-efficient alternative to std::set. It uses a bitset internally to track presence, offering $O(1)$ lookups and insertions.

The magic_enum::containers::set interface mirrors std::set, providing methods like insert, erase, and contains.

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

enum class Permission { READ, WRITE, EXECUTE };

int main() {
magic_enum::containers::set<Permission> user_perms;

// Add permissions to the set.
user_perms.insert(Permission::READ);
user_perms.insert(Permission::WRITE);

// Check for existence.
if (user_perms.contains(Permission::READ)) {
std::cout << "User has read access." << std::endl;
}

assert(user_perms.size() == 2);
assert(!user_perms.contains(Permission::EXECUTE));

return 0;
}