Skip to content

Reflected, compile-time, strongly-typed, header-only enum library

Notifications You must be signed in to change notification settings

Ceffa93/modern_enum

Repository files navigation

Modern Enum

Modern Enum is an enum library intended as a replacement for C++ enum classes.
It introduces two important features:

  • Reflection: the program can access enum size and element names, enabling logging of useful debug messages and serialization.
  • Type-safe flags: you can manipulate sets of flags in a robust, type-safe way, instead of casting to an unsigned integer.

Modern Enum aims to be efficient and easy to work with:

  • Easy to integrate: drop-in header-only library.
  • Compile-time: the library is 100% constexpr.
  • Ease of debug: human-readable Visual Studio debugging experience. natvis

Get Started

To quickly get started:

  • Build modern_enum.sln: compiles the project and runs the unit-tests in test.cpp;
  • Run modern_enum.sln: executes the sample code in main.cpp.

Integration

To integrate the library in your project:

  • Include modern_enum.h;
  • Include modern_enum.natvis (if using VS debugger);
  • Set C++ standard to c++17 or later;
  • Set /Zc=preprocessor flag (on MSVC).

API showcase

Define an enum:
MODERN_ENUM(
    Week,
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
);
Query enum reflection info:
constexpr auto name = Week::GetName(); // "Week"
constexpr auto count = Week::GetCount(); // 7
Access elements:
constexpr Week monday = Week::Monday;
Create elements from reflection:
constexpr Week tuesday = Week::FromIndex(1);
constexpr Week wednesday = Week::FromString("Wednesday");
Query element reflection info:
constexpr auto index = Week::Monday.index(); // 0
constexpr auto name = Week::Monday.toString(); // "Monday"
Iterate over elements:
for (Week m : Week::GetElements()) {}
Switch statement:
switch (Week::Wednesday) {
    case Week::Monday: 
    case Week::Tuesday: 
    ...
}
Flag set creation:
constexpr Week::Set weekend = Week::Saturday | Week::Sunday;
constexpr Week::Set monday = Week::Monday;
Set operators:
constexpr Week::Set weekdays = ~weekend;
constexpr Week::Set week = weekdays | weekend;
Set increment operators:
Week::Set days = Week::Monday;
days |= Week::Saturday;
days ^= weekdays;
Set query methods:
constexpr auto count = weekdays.count(); // 5
constexpr auto contains = weekdays.contains(monday); // true
constexpr auto none = weekdays.none(); // false
constexpr auto any = weekdays.any(); // true
constexpr auto all = weekdays.all(); // false

Limitations / future work

Currently, it is not possible to assign custom values to enum elements:

MODERN_ENUM(
    Example, 
    NoValue,
    Value = 5 // not supported
);

This is an intentional choice, because it is not possible to support such feature without degrading the quality of the API. As an example, this is the tradeoff used by the popular Better Enums library to support custom values:

// Static members are NOT of type "Week". 
// Internal raw-enum type is leaking
auto mondayInternalType = Week::Monday; 
mondayInternalType.toString(); // does not compile

// "+" operator required to convert internal type
Week monday = +mondayInternalType;
(+mondayInternalType).toString();

There is another way to support custom values without exposing internal types to the user, but at the cost of a more complex enum definition. I could consider adding this as an optional extension of the Modern Enum API:

MODERN_ENUM_WITH_CUSTOM_VALUES(
    Example, 
    WITHOUT_VALUE(NoValue),
    WITH_VALUE(Value, 5)
);

About

Reflected, compile-time, strongly-typed, header-only enum library

Resources

Stars

Watchers

Forks

Languages