SkillAgentSearch skills...

Enchantum

Faster enum reflection for C++17 since I don't want to wait for C++26 reflection.

Install / Use

/learn @ZXShady/Enchantum

README

Enchantum

Enchantum (short for "enchant enum") is a modern C++17 header-only library for compile-time enum reflection. It provides fast, lightweight access to enum values, names, and bitflags all without macros or boilerplate.

Every year, countless turtles perish due to the pollution caused by slow, bloated build times.
Save the turtles — and your compile times — by switching to enchantum!

<sup><sub>Source: I made it up.</sup></sub>

Key Features

  • Macro-free and non-intrusive boilerplate-free reflection

  • Fast compile times (benchmarked below)

  • Most efficient binary size wise.

  • 0 Allocations

  • Supports:

    • Scoped enums
    • Unscoped C enums
    • Sparse enums
    • Bitflags
    • Iteration over values,names and [enum, name] pairs.
    • string <=> enum conversions
    • enum <=> index conversions
    • enum validation functions like cast and contains
    • enum aware containers: enchantum::bitset and enchantum::array
  • Extra features like:

    • Scoped Functions
    • Optional null terminator disabling
    • Optional prefix stripping for C-style enums
    • 0 values are reflected for bitflag enums

Simple Examples

Why another enum reflection library?

Features

Benchmarks

Limitations

CMake Integration

Tested locally on Windows 10 with:

  • Visual Studio 2022 (19.44)
  • GCC 14.2.0
  • Clang 20.1.2

Compiler Support: (Look at CI)

  • GCC >= 9
  • Clang >= 8
  • MSVC >= 19.24 VS16.4 (lower verions tested through godbolt)

Tested through basic tests on godbolt since I could not install it on CI

  • ICX >= 2021.1.2 (Tested through godbolt test link)
  • NVC++ >= 22.7 (minimum on godbolt test link)

[!IMPORTANT] Be sure to read Limitations before using enchantum.

Simple Examples

  • to string
#include <enchantum/enchantum.hpp> // to_string
#include <enchantum/iostream.hpp> // iostream support
enum class Music { Rock, Jazz , Metal };

int main() 
{
  auto music = Music::Rock;
  std::string_view music_name =  enchantum::to_string(music);
  // music_name == "Rock"

  using namespace enchantum::iostream_operators;
  std::cout << music;
  // Prints Rock
}
  • from strings
#include <enchantum/enchantum.hpp> // cast
enum class Music { Rock, Jazz , Metal };
int main() 
{
  // case sensitive
  std::optional<Music> music = enchantum::cast<Music>("Jazz");
  if(music.has_value()) // check if cast succeeded
  {
    // *music == Music::Jazz
  }
  // pass a predicate taking two string views
  music = enchantum::cast<Music>("JAZZ",[](char x,char y){
      using UC = unsigned char;
      return std::tolower(UC(x)) == std::tolower(UC(y));      
  });
  if(music.has_value()) {
      // *music == Music::Jazz
  }
}
  • index into enums
#include <enchantum/enchantum.hpp> // index_to_enum and enum_to_index
enum class Music { Rock, Jazz , Metal };

int main() 
{
  // case sensitive
  std::optional<Music> music = enchantum::index_to_enum<Music>(1); // Jazz is the second enum member
  if(music.has_value()) // check if index is not out of bounds
  {
    // *music == Music::Jazz
    std::optional<std::size_t> index = enchantum::enum_to_index(*music);
    // *index == 1
  }
}
  • iteration
#include <enchantum/enchantum.hpp> // entries,values and names 
enum class Music { Rock, Jazz , Metal };

int main() 
{
  // Iterate over values
  for(Music music : enchantum::values_generator<Music>)
    std::cout << static_cast<int>(music) << " ";
  // Prints "0 1 2"

  // Iterate over names
  for(std::string_view name : enchantum::names_generator<Music>)
    std::cout << name << " ";
  // Prints "Rock Jazz Metal"

  // Iterate over both!
  for(const auto [music,name] : enchantum::entries_generator<Music>)
    std::cout << name << " = " << static_cast<int>(music) << "\n";
  
  // Prints 
  // Rock = 0
  // Jazz = 1
  // Metal = 2
}

Look at Features for more information.

Why Another Enum Reflection Library?

There are several enum reflection libraries out there — so why choose enchantum instead of magic_enum, simple_enum, or conjure_enum?

magic_enum

Pros

  • Macro-free (non intrusive).
  • No modifications needed for existing enums.
  • Allows specifying ranges for specific enums when needed.
  • Supports C++17.
  • Nicer compiler errors.
  • Supports wide strings.
  • Efficient executable binary size.
  • Null terminated strings.
  • 0 Allocations

Cons

  • Compile times grow significantly with larger MAGIC_ENUM_MAX_RANGE.
  • No warnings for not fully reflected enums.

conjure_enum

Pros

  • Macro-free (non intrusive)
  • Uses C++20

Cons

  • Slow compilation speed
  • Bigger executable sizes
  • Non optimal algorithms
  • Allocations
  • No warnings for not fully reflected enums.

simple_enum

Pros

  • Faster compile times.
  • Functor based API.
  • 0 Allocations

Cons

  • Requires specifying enum first/last values manually (intrusive, doesn't work well with third-party enums)
  • Compile time slows down with large enum ranges
  • Big binary size bloat.
  • No support for bitflags yet.
  • No support for null terminated strings.
  • No warnings for not fully reflected enums.

enchantum

Pros

  • Macro-free (non intrusive)
  • Allows specifying ranges for specific enums when needed
  • Compiles fast.
  • Supports C++17.
  • Clean and Simple Functor based API enchantum::to_string(E) no enchantum::to_string<E::V>() since compile times are fast.
  • Features like disabling null termination if not needed and specifying common enum prefix for C style enums, and reflect '0' values for bit flag enums.
  • Supports all sort of enums (scoped,unscoped,C style unfixed underlying type,anonymous namespaced enums, enums with commas in their typename,etc..)
  • Most efficient object binary size and executable size.
  • Null terminated strings.
  • 0 Allocations
  • Compiler errors for not fully reflected enums.

Cons

  • No support for wide strings.

Benchmarks

Summary

enchantum significantly reduces compile times and binary sizes in enum reflection projects. In my own project (which uses libassert and enum reflection for configuration), switching from magic_enum reduced full rebuild times from about 2 minutes to 1 minute and 26 seconds (34 seconds difference). I also tried compiling my project using [-2048,2048] as my range and it took 1 minute and 46 seconds! that's still less than magic_enum by default while having 16x the default range.

Each compile time benchmark was run 10 times and averaged unless noted otherwise. range is ENCHANTUM_MAX_RANGE and MAGIC_ENUM_RANGE_MAX, for simple_enum it is defining last and first with the range because I could not find a macro for this, this is technically misuse of the library since it likes having these values close to the actual range but the comparisons would be unfair.

The enum members are from 0 to Count

| Test Case | Small | Big | Large Range | Ideal Range | |------------------------|------------|--------------|-------------|-------------| | Number of Enums | 200 | 32 | 200 | 100 | | Enum Reflection Range | (-128,128) | (-256,256) | (-1024,1024)| (0,50) | | Enum members from 0 to | 16 | 200 | 16 | 50 |

Compile Time

All times in seconds (lower is better, bold is fastest). Compiled with -O3 fir GCC and Clang while /Ox for MSVC.

"Timeout" means it took more than 20 minutes and still did not finish

Notes: numbers in () are with ENCHANTUM_CHECK_OUT_OF_BOUNDS_BY set to the default which is 2. in short terms it is extra compile time only checks against not fully reflected enums which can be disabled by setting it to 0.

Conjure enum was compiled with FIX8_CONJURE_ENUM_MINIMAL macro defined.

| Compiler | Test Case | enchantum | magic_enum | simple_enum | conjure_enum | |-------------|-------------|-----------------|--------------|---------------|----------------| | GCC | Small | 6.0 (9.3) | 47 | 21.5 | 97.1 | | | Big | 2.7 (4.6) | 21 | 6.3 | 81.7 | | | Large Range | 15.9 (45.1) | Timeout | 313 | Unknown | | | Ideal Range | 3 (4.2) | 8.1 | 2.7 | 46.8 | | | | | Clang | Small | 5.8 (8.7) | 47 | 14 | 66.8 | | | Big | 2.3 (3.6) | 18 | 4.4 | 52.9 | | | Large Range | 15.1 (36.6) | Timeout | 96.3 | Timeout | | | Ideal Range | 2.9 (3.5) | 8.7 | 2.3 | 32 | | | | MSVC | Small | 15.8 (50.7) | 80 | 186 | ERROR | | | Big | 8.8 (13.6) | 37 | 32.1 | 244.9 | | | Large Range | 85.3 (265.1)| Timeout | Timeout | Timeout | | | Ideal Range | 5.8 (18.1) | 17.9 | 4.7 | 95.7 |

conjure_enum in "Small" test case caused the compiler to emit

fatal error C10

Related Skills

View on GitHub
GitHub Stars117
CategoryDevelopment
Updated12d ago
Forks10

Languages

C++

Security Score

100/100

Audited on Mar 19, 2026

No findings