Iterate, dispatch, and store enum values
When you need to perform actions for every member of an enumeration or store data associated with specific enum keys, standard C++ often requires manual maintenance of loops and arrays. The magic_enum library provides utilities to automate these tasks, ensuring that your logic remains synchronized with your enum definitions.
Compile-time Iteration
If you need to execute logic for every value in an enum—such as registering handlers or generating UI elements—magic_enum::enum_for_each provides a constexpr way to iterate over the entire range.
The function takes a callable that receives a magic_enum::detail::enum_constant wrapper. Because this wrapper is an object and not the enum value itself, you must invoke it as val() 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 };
int main() {
// Iterate over all Color values and print their names.
magic_enum::enum_for_each<Color>([](auto val) {
// val is a wrapper; call val() to get the enum value for enum_name.
std::cout << magic_enum::enum_name(val()) << " ";
});
// Output: Red Green Blue
return 0;
}
Type-Safe Dispatching
Traditional switch statements can be error-prone, especially when you need to return values and want to ensure all cases are handled. magic_enum::enum_switch acts as a functional replacement for a switch block, allowing you to dispatch logic based on a runtime enum value.
To ensure safety, you should specify an explicit result type (like std::string). This prevents magic_enum::enum_name from returning a std::string_view that might point to a null pointer if an invalid enum value is encountered. The lambda must also declare a trailing return type matching the result type.
#include <iostream>
#include <string>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_switch.hpp>
enum class Status { Active, Suspended, Terminated };
int main() {
Status s = Status::Suspended;
// Dispatch based on the status value.
auto description = magic_enum::enum_switch<std::string>([](auto val) -> std::string {
if constexpr (val() == Status::Active) {
return "System is running.";
} else if constexpr (val() == Status::Suspended) {
return "System is paused.";
} else {
return "System is offline.";
}
}, s);
std::cout << "Status: " << description << std::endl;
// Output: Status: System is paused.
return 0;
}
Enum-Keyed Arrays
Storing data associated with enum values usually involves std::array, but managing the mapping between enum values and integer indices is manual. magic_enum::containers::array automates this by providing a container that is directly indexed by the enum type.
The container size is automatically determined by magic_enum::enum_count<E>(). You should default-construct the array and then assign values using the enum keys via operator[].
#include <iostream>
#include <string>
#include <magic_enum/magic_enum.hpp>
#include <magic_enum/magic_enum_containers.hpp>
enum class Level { Low, Medium, High };
int main() {
// Create an array mapping Level to a descriptive string.
magic_enum::containers::array<Level, std::string> level_names;
// Assign values using enum keys.
level_names[Level::Low] = "Basic Access";
level_names[Level::Medium] = "Standard Access";
level_names[Level::High] = "Administrator Access";
Level current = Level::Medium;
std::cout << "Permissions: " << level_names[current] << std::endl;
// Output: Permissions: Standard Access
return 0;
}
Efficient Enum Sets
When you need to track a collection of unique enum values—such as active flags or selected options—magic_enum::containers::set provides a std::set-like interface optimized for enums. Internally, it uses a bitset for high performance and minimal memory footprint.
You can populate the set using initializer lists or the insert method, and check for membership using contains.
#include <iostream>
#include <magic_enum/magic_enum_containers.hpp>
enum class Feature { Logging, Encryption, Compression, Debugging };
int main() {
// Define a set of enabled features.
magic_enum::containers::set<Feature> enabled_features = {Feature::Logging, Feature::Encryption};
// Dynamically add a feature.
enabled_features.insert(Feature::Compression);
// Check for feature presence.
if (enabled_features.contains(Feature::Encryption)) {
std::cout << "Security is enabled." << std::endl;
}
if (!enabled_features.contains(Feature::Debugging)) {
std::cout << "Debug mode is disabled." << std::endl;
}
return 0;
}