A modern C++17 header-only enum library with compile-time operations, rich metadata, STL integration, and customizable policies. All features work at compile-time with full constexpr support.
- Full Compile-Time Support -
constexpreverything: lookup, iteration, validation - Rich Metadata - Attach
value,desc,extrafields to each enum element - Auto-Increment - Automatic value filling with customizable increment policies
- Powerful Lookup -
fromValue()andfromName()withstd::optional - STL Integration - Works seamlessly with containers, algorithms, and iterators
- Custom Policies - Define your own increment, search, and comparison behaviors
- Type-Safe - Strong typing with comparison operators and switch statement support
- Header-Only - No linking required, just include and use
We can declare enums in a traditional way, with fields or mix.
#include <trlc/enum.hpp>// Basic enum TRLC_ENUM(Color, Red, Yellow, Green);// Enum with rich metadata: value, desc, extra TRLC_ENUM(HttpStatus, OK = TRLC_FIELD(value = 200, desc = "Request successful", extra = "2xx"), Created = TRLC_FIELD(value = 201, desc = "Resource created successfully", extra = "2xx"), BadRequest = TRLC_FIELD(value = 400, desc = "Invalid request syntax", extra = "4xx"));// We can declare enum constants with any combination of fields: value, desc, extra in any order TRLC_ENUM(Fields, None, Value = TRLC_FIELD(value = 5), Desc = TRLC_FIELD(desc = "With Description"), Extra = TRLC_FIELD(extra = "Extra Info"), All = TRLC_FIELD(value = 10, desc = "All Fields", extra = "Full"));// Enum with custom types: std::string_view for value, desc, extra constexpr bool case_sensitive{false}; using FilePolicies = trlc::enums::policies:: DefaultPolicies<std::string_view, std::string_view, std::string_view, case_sensitive>; TRLC_ENUM_DEF(FileType, FilePolicies, Json = TRLC_FIELD(value = ".json", desc = "JSON file", extra = "application/json"), Xml = TRLC_FIELD(value = ".xml", desc = "XML file", extra = "application/xml"), Yaml = TRLC_FIELD(value = ".yaml", desc = "YAML file", extra = "application/x-yaml"))This Enum is essentially a struct, so we can declare it within the scope where the struct can be declared.
// We can use attributes like name(), value(), desc(), extra(), enumName() on enum instances static_assert(HttpStatus::OK.value() == 200); static_assert(HttpStatus::Created.desc() == "Resource created successfully"); static_assert(HttpStatus::BadRequest.extra() == "4xx"); static_assert(HttpStatus::OK.enumName() == "HttpStatus"); constexpr auto green{Color::Green}; static_assert(green.name() == "Green"); static_assert(green.value() == 2); static_assert(green.enumName() == "Color"); std::cout << "Compile time attributes check passed." << std::endl;// We can use the functions fromValue and fromString to lookup the enum element. // The return value will be constexpr std::optional<enum> constexpr auto status_opt{HttpStatus::fromValue(201)}; static_assert(status_opt.has_value()); static_assert(status_opt.value() == HttpStatus::Created); // Look up with case-insensitive constexpr auto filetype_opt{FileType::fromName("XML")}; static_assert(filetype_opt.has_value()); static_assert(filetype_opt.value() == FileType::Xml); std::cout << "Compile time fromValue(), fromString() check passed." << std::endl;// We can use comparison operators constexpr auto yellow{Color::Yellow}; static_assert(yellow != Color::Green); static_assert(yellow > Color::Red); static_assert(yellow < Color::Green); static_assert(yellow == Color::Yellow); std::cout << "Compile time comparison operators check passed." << std::endl;// Range-based for loop constexpr std::size_t num_colors = [] { std::size_t count = 0; for ([[maybe_unused]] const auto& light : Color::all()) { ++count; } return count; }(); static_assert(num_colors == Color::SIZE); std::cout << "Compile time range-based for loop check passed." << std::endl;// Explicit iterators std::cout << HttpStatus::NAME << " :\n"; for (auto it = HttpStatus::begin(); it != HttpStatus::end(); ++it) { std::cout << " - " << it->name() << " = " << it->value() << " (" << it->desc() << ", " << it->extra() << ")\n"; }// STL containers compatibility auto found = std::find_if(Fields::begin(), Fields::end(), [](auto f) { return f == Fields::All; }); assert(found != Fields::end()); assert(*found == Fields::All); std::vector<Color::Enum> lights{}; lights.push_back(Color::Green); lights.push_back(Color::Red); lights.push_back(Color::Yellow); lights.push_back(Color::Green); std::cout << "Light Sequence: "; for (const auto& light : lights) { std::cout << light.name() << " "; }Terminal Output:
Compile time attributes check passed. Compile time fromValue(), fromString() check passed. Compile time comparison operators check passed. Compile time range-based for loop check passed. HttpStatus : - OK = 200 (Request successful, 2xx) - Created = 201 (Resource created successfully, 2xx) - BadRequest = 400 (Invalid request syntax, 4xx) Light Sequence: Green Red Yellow GreenYou can see the full basic usage example at [basic_usage]
// Use case: Store custom struct type as extra metadata (e.g., RGB color values) // Features: Compile-time custom types, type-safe metadata, constexpr validation struct RGB { uint8_t r; uint8_t g; uint8_t b; };// Custom extra field type with default policies // NOTE: extra = (RGB{255, 0, 0}) syntax requires parentheses to avoid parsing issues constexpr bool case_sensitive{false}; using ColorPolicies = trlc::enums::policies::DefaultPolicies<int, std::string_view, RGB, case_sensitive>; TRLC_ENUM_DEF(Color, ColorPolicies, Red = TRLC_FIELD(extra = (RGB{255, 0, 0})), Green = TRLC_FIELD(extra = (RGB{0, 255, 0})), Blue = TRLC_FIELD(extra = (RGB{0, 0, 255})));// Extra Metadata can work as normal struct static_assert(Color::Red.extra().r == 255); static_assert(Color::Red.extra().g == 0); static_assert(Color::Red.extra().b == 0); static_assert(Color::Green.extra().r == 0); static_assert(Color::Green.extra().g == 255); static_assert(Color::Green.extra().b == 0); static_assert(Color::Blue.extra().r == 0); static_assert(Color::Blue.extra().g == 0); static_assert(Color::Blue.extra().b == 255); std::cout << "Custom extra metadata RGB values verified at compile time." << std::endl; // Iterate over all Color enum values and print their names and RGB extras for (const auto& p : Color::all()) { std::cout << p.name() << ": (" << static_cast<int>(p.extra().r) << ", " << static_cast<int>(p.extra().g) << ", " << static_cast<int>(p.extra().b) << ")\n"; }Terminal Output
Custom extra metadata RGB values verified at compile time. Red: (255, 0, 0) Green: (0, 255, 0) Blue: (0, 0, 255)You can see the full custom extra example at [custom_extra]
// Use case: Bit flags for permissions, options, etc. // Features: Auto-increment as powers of 2 (1, 2, 4, 8, ...) template <typename T> struct PowerOf2Increment { static constexpr T startValue() { return 0x01; } static constexpr T increment(T current, std::size_t /* index */) { return current * 2; } }; template <typename T> struct PowerOf2Policies { using ValueType = T; using DescType = std::string_view; using ExtraType = std::string_view; inline static constexpr bool CASE_SENSITIVE{true}; using Increment = PowerOf2Increment<ValueType>; using Search = LinearSearchPolicy<ValueType, CASE_SENSITIVE>; using Comparison = LexicographicComparisonPolicy<ValueType, CASE_SENSITIVE>; }; TRLC_ENUM_DEF(Permission, PowerOf2Policies<uint32_t>, Read, // 0x01 (auto) Write, // 0x02 (auto) Execute, // 0x04 (auto) Delete // 0x08 (auto) );You can see the full basic usage example at [basic_usage]
To use this library, you need:
- CMake 3.10 or higher
- GCC, Clang or MSVC compiler with C++17 support
- GoogleTest (automatically fetched by CMake for testing)
This library can be used as CMake subdirectory.
- Fetch it, e.g. using [git submodules]:
git submodule add https://github.com/tranglecong/trlc_enum git submodule update --init --recursiveOr you can use git clone: git clone https://github.com/tranglecong/trlc_enum.git
-
Call
add_subdirectory(path_to/trlc_enum)or whatever your local path is to make it available in [CMake] file. -
Simply call
target_link_libraries(your_target PUBLIC trlc::enum)to link this library and setups the include search path and compilation options.
You can also install trlc_enum library
-
Run CMake configure inside the library sources. If you want to build the UT and example set
-DTRLC_BUILD_TESTS=ON,-DTRLC_BUILD_EXAMPLES=ONcmake -DCMAKE_BUILD_TYPE=Debug -DTRLC_BUILD_TESTS=OFF -DTRLC_BUILD_EXAMPLES=OFF -DTRLC_GENERATE_RECURSIVE_MACRO=ON -S . -B ./build
The Enum library uses a recursive macro. The header macros will be generated when running the CMake configure through the execute Python script [macro_expansion_generator.py]. The default value of
TRLC_MACRO_RECURSIVE_MAX_NUMis 64. If you want to change it, you can modify the CMake file or set-DTRLC_MACRO_RECURSIVE_MAX_NUM=xxx. With xxx being the number you desire.
-
Build and install the library under
${CMAKE_INSTALL_PREFIX}. You may be required to have sudo privileges to install in the/usr/*.cmake --build ./build -j8 -- install
[Optional] if you want to run UT.
ctest --test-dir ./build
-
To use an installed library.
find_package(trlc REQUIRED) target_link_libraries(your_target PUBLIC trlc::enum)
Welcome contributions from everyone! If you’d like to help improve this project. Thank you for considering contributing to this project!