Skip to main content

Inspect and validate flag enums

When working with bit-flag enums in C++, magic_enum provides utilities to format these values into human-readable strings and to validate if a given value represents a valid flag or combination of flags. To leverage these features, you must first declare your enum as a "flag enum" and include the magic_enum/magic_enum_flags.hpp header.

To declare an enum as a flag enum, specialize magic_enum::customize::enum_range<E> for your enum type E and set is_flags = true;. It is crucial that the underlying enum values are powers of two for individual flags to ensure proper bitwise combination.

magic_enum::enum_flags_name

The magic_enum::enum_flags_name free function converts a bit-flag enum value into its string representation. This is particularly useful for debugging, logging, or displaying combined flag states.

The function concatenates the names of individual flags present in the enum value, using a pipe (|) as the default separator. The order of flag names in the output string corresponds to their declaration order within the enum. If the input value is 0 or contains bits that do not correspond to any defined flag, enum_flags_name returns an empty string.

#include <iostream> // For std::cout
#include <string> // For std::string
#include <type_traits> // For std::underlying_type_t
#include "magic_enum/magic_enum_flags.hpp"

// Define a Color enum with flag values
enum class Color {
RED = 1 << 0, // 1
GREEN = 1 << 1, // 2
BLUE = 1 << 2 // 4
};

// Declare Color as a flag enum for magic_enum
template <>
struct magic_enum::customize::enum_range<Color> {
static constexpr bool is_flags = true;
};

// Define a C-style flag enum
enum CStyleFlags {
CStyleFlags_A = 1 << 0,
CStyleFlags_B = 1 << 1,
CStyleFlags_C = 1 << 2,
CStyleFlags_D = 1 << 3,
};

// Declare CStyleFlags as a flag enum and specify prefix_length
template <>
struct magic_enum::customize::enum_range<CStyleFlags> {
static constexpr bool is_flags = true;
static constexpr auto prefix_length = sizeof("CStyleFlags_") - 1;
};

int main() {
// Make magic_enum::bitwise_operators visible for operator|
using namespace magic_enum::bitwise_operators;

// Basic usage with enum class
Color combined_color = Color::RED | Color::GREEN;
std::cout << "Combined Color: " << magic_enum::enum_flags_name(combined_color) << std::endl; // Output: RED|GREEN

// Single flag
std::cout << "Single Color: " << magic_enum::enum_flags_name(Color::BLUE) << std::endl; // Output: BLUE

// Value with undefined bits or zero
Color invalid_color = Color::RED | static_cast<Color>(1 << 3); // 1 | 8
std::cout << "Invalid Color: '" << magic_enum::enum_flags_name(invalid_color) << "'" << std::endl; // Output: '' (empty string)

Color zero_color = static_cast<Color>(0);
std::cout << "Zero Color: '" << magic_enum::enum_flags_name(zero_color) << "'" << std::endl; // Output: '' (empty string)

// Usage with C-style flags and prefix stripping
CStyleFlags c_flags = CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_D;
std::cout << "C-style Flags: " << magic_enum::enum_flags_name(c_flags) << std::endl; // Output: A|B|C|D

return 0;
}

magic_enum::enum_flags_contains

The magic_enum::enum_flags_contains free function verifies if a given enum value, integer, or string represents a valid flag or combination of flags for a specified enum type. It returns true if the input can be successfully cast to a valid flag enum value, and false otherwise. This includes cases where the value is 0 or contains undefined bits.

When calling enum_flags_contains with an integer or string argument, the enum template argument E must be explicitly specified. For example, magic_enum::enum_flags_contains<Status>(5) or magic_enum::enum_flags_contains<Status>("Active"). The enum type cannot be deduced automatically from these overloads. For string inputs, enum_flags_contains can parse multiple flags separated by | and supports custom comparison predicates for case-insensitive matching or other specific needs.

#include <iostream> // For std::cout, std::boolalpha
#include <string> // For std::string, std::string_view
#include <cctype> // For std::tolower
#include <type_traits> // For std::underlying_type_t
#include "magic_enum/magic_enum_flags.hpp"

// Define a Status enum with flag values
enum class Status {
NONE = 0,
ACTIVE = 1 << 0, // 1
PENDING = 1 << 1, // 2
COMPLETED = 1 << 2 // 4
};

// Declare Status as a flag enum for magic_enum
template <>
struct magic_enum::customize::enum_range<Status> {
static constexpr bool is_flags = true;
};

int main() {
std::cout << std::boolalpha; // Print bools as true/false

// Make magic_enum::bitwise_operators visible for operator|
using namespace magic_enum::bitwise_operators;

// Checking with an enum value
Status s1 = Status::ACTIVE | Status::PENDING;
std::cout << "Contains ACTIVE | PENDING: " << magic_enum::enum_flags_contains(s1) << std::endl; // Output: true

Status s2 = Status::ACTIVE | static_cast<Status>(1 << 3); // ACTIVE | undefined bit
std::cout << "Contains ACTIVE | undefined: " << magic_enum::enum_flags_contains(s2) << std::endl; // Output: false

Status s3 = Status::NONE;
std::cout << "Contains NONE: " << magic_enum::enum_flags_contains(s3) << std::endl; // Output: false (0 is not a valid flag combination)

// Checking with an integer value (explicit template argument required)
std::cout << "Contains integer 1 (ACTIVE): " << magic_enum::enum_flags_contains<Status>(1) << std::endl; // Output: true
std::cout << "Contains integer 3 (ACTIVE | PENDING): " << magic_enum::enum_flags_contains<Status>(1 | 2) << std::endl; // Output: true
std::cout << "Contains integer 5 (ACTIVE | COMPLETED): " << magic_enum::enum_flags_contains<Status>(1 | 4) << std::endl; // Output: true
std::cout << "Contains integer 8 (undefined): " << magic_enum::enum_flags_contains<Status>(8) << std::endl; // Output: false
std::cout << "Contains integer 0 (NONE): " << magic_enum::enum_flags_contains<Status>(0) << std::endl; // Output: false

// Checking with a string value (explicit template argument required)
std::cout << "Contains string 'ACTIVE|COMPLETED': " << magic_enum::enum_flags_contains<Status>("ACTIVE|COMPLETED") << std::endl; // Output: true
std::cout << "Contains string 'PENDING': " << magic_enum::enum_flags_contains<Status>("PENDING") << std::endl; // Output: true
std::cout << "Contains string 'ACTIVE|UNKNOWN': " << magic_enum::enum_flags_contains<Status>("ACTIVE|UNKNOWN") << std::endl; // Output: false
std::cout << "Contains string 'UNKNOWN': " << magic_enum::enum_flags_contains<Status>("UNKNOWN") << std::endl; // Output: false

// Checking with a string value and custom predicate (case-insensitive)
auto case_insensitive_compare = [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); };
std::cout << "Contains string 'active|completed' (case-insensitive): " << magic_enum::enum_flags_contains<Status>("active|completed", case_insensitive_compare) << std::endl; // Output: true

return 0;
}

Gotchas and Warnings

  • magic_enum::enum_flags_name returns an empty string if the input enum value is 0 or contains bits that do not correspond to any defined flag.
  • magic_enum::enum_flags_contains returns false if the input value (enum, integer, or string) does not represent a valid flag or combination of flags for the specified enum type, including when the value is 0.
  • When calling magic_enum::enum_flags_contains with an integer or string argument, the enum template argument E must be explicitly specified (e.g., enum_flags_contains<MyEnum>(5) or enum_flags_contains<MyEnum>("FlagA")). It cannot be deduced automatically from these overloads.
  • The order of flag names in the string returned by magic_enum::enum_flags_name is determined by their declaration order in the enum, not necessarily by their numeric value.