Enum To String C++26 Reflection Compile-Time Library

A C++26 Compile-Time Enum Reflection Library

Github ↗

The Program


#include <iostream>

int main()
{
    enum class ObjectId
    {
        enum0,
        enum1,
        enum2,
        enum3,
        enum4,
        enum5,
        enum6
    };


    using ObjectIdInfo =
        EnumInfo<ObjectId>;


    std::cout
        << "Enum size: "
        << ObjectIdInfo::Size()
        << "\n\n";


    ObjectId id =
        ObjectIdInfo::MakeEnum(
            "enum5");


    std::cout
        << "enum5 = "
        << static_cast<int>(id)
        << '\n';


    std::cout
        << "enum3 = "
        << ObjectIdInfo::ToString(
            ObjectId::enum3)
        << '\n';


    std::cout
        << "\nList Enums:\n";


    ObjectIdInfo::ForEach(
        PrintEnumEntry{});
}

The Library


namespace EnumReflection
{

// ============================================================
// ENUM ENTRY
// ============================================================

template<typename Enum>
struct EnumEntry
{
    Enum value;
    std::string_view name;
};


// ============================================================
// REFLECTION
// ============================================================

template<typename Enum>
constexpr auto GetEnumerators()
{
    return std::define_static_array(
        std::meta::enumerators_of(
            ^^Enum));
}


// ============================================================
// ENUM TABLE GENERATION
// ============================================================

template<typename Enum>
consteval auto GenerateEnumTable()
{
    constexpr auto enumerators =
        GetEnumerators<Enum>();


    std::array<
        EnumEntry<Enum>,
        enumerators.size()> entries{};


    size_t index = 0;


    template for
    (
        constexpr auto enumerator :
        [: std::meta::reflect_constant_array(
            enumerators) :]
    )
    {
        entries[index++] =
        {
            [:enumerator:],
            std::meta::identifier_of(
                enumerator)
        };
    }


    return entries;
}


// ============================================================
// ENUM INFORMATION
// ============================================================

template<typename Enum>
struct EnumInfo
{
    static constexpr auto entries =
        GenerateEnumTable<Enum>();



    static constexpr std::string_view ToString(
        Enum value,
        std::string_view fallback = "none")
    {
        for(auto&& entry : entries)
        {
            if(entry.value == value)
                return entry.name;
        }

        return fallback;
    }



    static constexpr Enum MakeEnum(
        std::string_view name,
        Enum fallback = Enum{})
    {
        for(auto&& entry : entries)
        {
            if(entry.name == name)
                return entry.value;
        }

        return fallback;
    }



    static constexpr size_t Size()
    {
        return entries.size();
    }



    template<typename Function>
    static constexpr void ForEach(
        Function&& function)
    {
        for(auto&& entry : entries)
        {
            function(
                entry.value,
                entry.name);
        }
    }
};

}

Tutorial

Traditional C++ enum conversion requires manually maintaining a second data structure:


enum class ObjectId
{
    Player,
    Enemy,
    Coin
};


constexpr std::pair<ObjectId, std::string_view> Names[] =
{
    { ObjectId::Player, "Player" },
    { ObjectId::Enemy,  "Enemy"  },
    { ObjectId::Coin,   "Coin"   }
};

The problem is that the enum and the lookup table can become inconsistent. Adding a new enum value requires updating multiple locations.

C++26 reflection removes this duplication by allowing the compiler to inspect the enum declaration itself.

Reflection as the Source of Truth

The enum declaration becomes the only required definition:


enum class ObjectId
{
    Player,
    Enemy,
    Coin
};

The compiler can now discover the enumerators:


std::meta::enumerators_of(
    ^^ObjectId);

This produces reflection metadata describing each enum constant. The library transforms that metadata into a compile-time lookup table.

The Compile-Time Pipeline


enum declaration

        |
        v

std::meta::enumerators_of()

        |
        v

std::define_static_array()

        |
        v

template for reflection expansion

        |
        v

std::array<EnumEntry>

        |
        v

EnumInfo<T>

The important idea is that no runtime registration occurs. The conversion table exists entirely during compilation.

Automatic Enum To String Conversion

Once generated, converting an enum value to text is simple:


auto name =
    ObjectIdInfo::ToString(
        ObjectId::Player);

The returned value is a compile-time generated std::string_view. No allocation is required.

String To Enum Conversion

The reverse operation is also generated automatically:


ObjectId id =
    ObjectIdInfo::MakeEnum(
        "Player");

This is useful for:

  • configuration files
  • serialization
  • editor tools
  • network protocols
  • debugging systems

Enumerating All Values

Because the metadata table exists at compile time, iterating over all enum values is trivial:


struct PrintEnumEntry
{
    template<typename Enum>
    void operator()(
        Enum value,
        std::string_view name) const
    {
        std::cout
            << static_cast<int>(value)
            << " -> "
            << name
            << '\n';
    }
};


ObjectIdInfo::ForEach(
    PrintEnumEntry{});

The visitor is completely generic and works with any enum type.

Why Use A Type Alias?

The template type itself is implementation detail. Applications normally define a convenient alias:


using ObjectIdInfo =
    EnumInfo<ObjectId>;

The rest of the program can now use:


ObjectIdInfo::ToString(
    ObjectId::Player);


ObjectIdInfo::MakeEnum(
    "Enemy");


ObjectIdInfo::ForEach(
    visitor);

Advantages

  • No macros
  • No duplicated enum tables
  • No runtime initialization
  • No reflection registration code
  • Strong type safety
  • Compile-time generated metadata
  • Works with any enum type

Future Extensions

The same reflection mechanism can be extended with additional metadata:

  • custom display names
  • serialization names
  • editor categories
  • flags and bitmask support
  • automatic UI generation

C++26 reflection turns enums from simple integer wrappers into fully inspectable compile-time objects. This library uses that capability to provide enum conversion and enumeration without requiring any manual maintenance.