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;
}