Skip to main content

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