/enumbra

A code generator for enums.

Primary LanguageC++MIT LicenseMIT

Getting Started

enumbra is a code generator for enums.

enumbra is in a volatile state of development where syntax and features may change at any time.
If you need stability, wait until the 1.0 release.

Please refer to the wiki for full documentation.

Annotated starter config files are provided in the examples directory.
Running the following command will parse the given config files and generate enumbra_test.hpp.

./enumbra.exe -c enumbra_config.json -s enum.json --cppout enumbra_test.hpp

Types

enumbra generates two core types of enums: Value Enum and Flags Enum.

A Value Enum always represents one singular state.

A Flags enum may represent no state, one state, or multiple states.

Enum Type State Bitwise Ops Bit Packing (C++)
Value Single Value No Yes
Flags None / Single / Multiple Yes Yes

Generators

C++

Generated C++ code requires a minimum of C++17. Additional features are conditionally enabled up to C++23.

Currently, enumbra's generated headers require ONE C++ standard library header: <cstdint>

Generated headers are self-sufficient, just drop them into your project and include them. There are no master headers or include order issues to worry about.

Including headers generated with different versions of enumbra will throw compiler warnings.
By default, only major differences in templates, macros, class layouts, and function definitions would generate warnings.

With ENUMBRA_STRICT_VERSION defined, all headers must be generated with the exact same enumbra version regardless of if they would be technically compatible. This is useful for making sure your build system is set up to properly regenerate all files.

Examples

Value Enum

// A class representing this enum is generated by enumbra
enum class Direction : uint8_t {
    NORTH = 0,
    EAST = 1,
    SOUTH = 2,
    WEST = 3
};
// < generated enumbra functions omitted >

// Usage Example
Direction dir = enumbra::default_value<Direction>(); // Constructed with user-defined default value
dir = Direction::WEST; // All the regular enum class features apply
switch (dir) {
    case Direction::NORTH: break;
    case Direction::EAST: break;
    case Direction::SOUTH: break;
    case Direction::WEST: break;
    default: break;
}

// Compile time programming
constexpr auto min = enumbra::min<Direction>(); // 0
constexpr auto max = enumbra::max<Direction>(); // 3 
constexpr auto count = enumbra::count<Direction>(); // 4
constexpr bool contiguous = enumbra::is_contiguous<Direction>(); // true
if constexpr (contiguous){
    // Information is known at compile time to produce good code-gen. 
    // Templates are available to make this easier - see wiki for details
}

// Not allowed - bitwise ops not defined for Value Enums
// dir |= Direction::EAST;

Flags Enum

// A class representing this enum is generated by enumbra
enum class FilePermissions : uint8_t {
    READ = 1,
    WRITE = 2,
    EXECUTE = 4,
    DELETE = 8
};
// < generated enumbra functions omitted >

// Usage Example
FilePermissions perms = enumbra::default_value<FilePermissions>(); // Constructed with user-defined default value
// Bitwise ops are generated and work like normal
perms = FilePermissions::READ | FilePermissions::WRITE;
perms |= FilePermissions::EXECUTE; 

// Test and set with functions as well
set(perms, FilePermissions::DELETE);
if(test(perms, FilePermissions::DELETE)){
    // ...
}
unset(perms, FilePermissions::READ);

// Compile time programming
constexpr auto min = enumbra::min<FilePermissions>(); // 0 = 0b0000
constexpr auto max = enumbra::max<FilePermissions>(); // 15 = 0b1111
constexpr auto count = enumbra::count<FilePermissions>(); // 4 total flags
constexpr bool contiguous = enumbra::is_contiguous<FilePermissions>(); // true
if constexpr (contiguous){
    // Information is known at compile time to produce good code-gen. 
    // Templates are available to make this easier - see wiki for details
}

Compact Bit Field Enums

See the Wiki for more info.

Other Languages

C#: Maybe eventually. Focus is on C++.

Building

This section refers to building enumbra itself, not the generated code. See the Generators section for generated code requirements.

enumbra requires a C++17 compiler and is primarily tested to build on Windows with Visual Studio 2022.

If you are on another OS/compiler and would like to add native support, open an issue/PR. I suck at cmake so don't expect any help.

enumbra uses vcpkg manifests for a couple of dependencies and should be automatically detected if VCPKG_ROOT is set.

Known Limitations

  1. Flags Enums are required to have an unsigned underlying type.
  2. Values within Flags Enums may not span multiple bits. A single value must control a single bit. (TODO: A separate mechanism for defining multi-bit flags should be added.)
  3. Values and names within an enum must be unique.

Q&A

Q. How does enumbra compare to magic_enum/Better Enums?

  • magic_enum and Better Enums are compile-time generators that work directly on C++ source. All they require is placing a header in your sources.
    • enumbra is a standalone code generator with its own configuration syntax that requires an additional build step.
  • enum code generation at compile time significantly slows down compile times / bloats memory. This cost is paid on every compile.
    • enumbra generates all code before compilation, which can then be compiled once.
    • Functions still benefit from constexpr where possible.
    • enumbra can analyze enums to provide extra functionality that is not possible with compile-time libraries.
  • The number of entries within a compile-time evaluated enum is usually limited to around 64-128 due to compiler limits for macros/templates.
    • enumbra has a default limit of 65536 entries within an enum. The limit can be easily increased through configuration.
  • Lack of configuration options.
    • enumbra has a few more knobs and optional validation steps. Reasonable defaults are set to work out of the box.
  • Limited to one programming language (C++).
    • enumbra can potentially generate to any language ... but currently only C++.
  • Better Enums are limited to enums that you control and define.
    • enumbra can be used to manually re-define third-party enums, though this is a bit dangerous as you get out of sync.
    • Some more investigation for enumbra is needed here.
    • magic_enum is the safer option here as it works directly on the original enum.
  • Minimum C++ Standard version
    • enumbra: C++17
    • magic_enum: C++17
    • Better Enums: C++11