DEV Community

Tráng Lê Công
Tráng Lê Công

Posted on

Introducing TRLC Enum: A Modern C++ Enum Library for Enhanced Usability

🚀 Overview

I’m thrilled to introduce trlc_enum, an open-source C++ library designed to push enums beyond their traditional limitations. By enhancing enums with attributes and supporting compile-time operations, trlc_enum is here to make C++ enums more powerful, traceable, and easy to use.

🔑 Features

Declaration

With trlc_enum, enums are declared with attributes such as value, desc, and tag. This design allows us to add detailed metadata to enums, enhancing both code readability and usability.

#include <trlc/enum.hpp>
Enter fullscreen mode Exit fullscreen mode
TRLC_ENUM(Rainbow,
          RED,
          ORANCE,
          YELLOW,
          GREEN,
          BLUE,
          INDIGO,
          VIOLET)
Enter fullscreen mode Exit fullscreen mode
TRLC_ENUM(Cars,
          SEDAN = TRLC_FIELD(value = 1, desc = "A comfortable car for daily commuting and family trips."),
          SUV = TRLC_FIELD(value = 2, desc = "A versatile vehicle built for various terrains and passenger capacity."),
          TRUCK = TRLC_FIELD(value = 3, desc = "A powerful vehicle designed for transporting heavy loads and equipment."),
          JEEP = TRLC_FIELD(value = 4, desc = "A rugged vehicle ideal for off-road adventures and exploration."))
Enter fullscreen mode Exit fullscreen mode
TRLC_ENUM(Validate,
          NON_FIELD,
          WITH_DEFAULT = TRLC_FIELD(value = 5),
          WITH_DESC = TRLC_FIELD(desc = "With description."),
          FULL_FIELD = TRLC_FIELD(value = 100, desc = "Full feild."),
          NEGATIVE_VALUE = TRLC_FIELD(value = -100, desc = "Default trlc enum can support negative value."),
          END)
Enter fullscreen mode Exit fullscreen mode

This Enum is essentially a struct, so we can declare it within the scope where the struct can be declared.

Attributes

Enum elements come with attributes like name, value, and desc that can be accessed at compile-time, making it easy to perform checks or validations as you code.

static_assert(Rainbow::ORANCE.tag() == "Rainbow");
static_assert(Rainbow::RED.value() == 0);
static_assert(Rainbow::GREEN.name() == "GREEN");
static_assert(Cars::JEEP.value() == 4);
static_assert(Cars::SUV.name() == "SUV");
static_assert(Cars::SEDAN.desc() == "A comfortable car for daily commuting and family trips.");
static_assert(Validate::NEGATIVE_VALUE.name() == "NEGATIVE_VALUE");
static_assert(Validate::NEGATIVE_VALUE.value() == -100);
static_assert(Validate::NEGATIVE_VALUE.desc() == "Default trlc enum can support negative value.");

std::cout << "Compile time attributes check passed." << std::endl;
Enter fullscreen mode Exit fullscreen mode

Conversion

Easily convert enums from values or strings with the fromValue and fromString methods.

constexpr auto rainbow_green_optional{Rainbow::fromValue(3)};
static_assert(rainbow_green_optional.has_value() == true);
static_assert(rainbow_green_optional.value() == Rainbow::GREEN);

constexpr auto cars_suv_optional{Cars::fromString("SUV")};
static_assert(cars_suv_optional.has_value() == true);
static_assert(cars_suv_optional.value() == Cars::SUV);

std::cout << "Compile time fromValue(), fromString() check passed." << std::endl;
Enter fullscreen mode Exit fullscreen mode

The return type is constexpr std::optional<enumtype>, making error handling straightforward..

Iterators

Enumerate over enums at both compile- and runtime using iterators.

constexpr auto check_size_of_rainbow = [&]() -> size_t
{
    auto size{0};
    for (auto elem : Rainbow::iterator)
    {
        size++;
    }
    return size;
};
static_assert(check_size_of_rainbow() == Rainbow::size());

std::cout << "Compile time iterators check passed." << std::endl;
Enter fullscreen mode Exit fullscreen mode

Traceability

From an enum element, we can also retrieve its holder.

constexpr auto suv{cars_suv_optional.value()};
static_assert(suv.tag() == "Cars");
static_assert(suv.holder().tag() == "Cars");
static_assert(suv.holder().TRUCK == Cars::TRUCK);

std::cout << "Compile time holder check passed." << std::endl;
Enter fullscreen mode Exit fullscreen mode

Each enum and element contains a tag() for its name and a dump() method for JSON representation of its properties, useful for debugging and data inspection.

std::cout << "[1] Enum Rainbow :";
std::cout << Rainbow::dump() << std::endl;

std::cout << "[2] Enum Cars :";
std::cout << Cars::dump() << std::endl;

std::cout << "[3] Enum Validate :\n";
// Of course, we can also use iterators to print the properties.
for (auto elem : Validate::iterator)
{
    std::cout << elem.dump() << std::endl;
}
Enter fullscreen mode Exit fullscreen mode

Customization

Currently, TRLC_ENUM uses trlc:DefaultEnumDef<>, but you can also define an enum definition and use it with TRLC_ENUM_DETAIL.

template<class Holder>
struct CustomEnumDefine
{
    using holder = Holder;
    using value_type = uint32_t;
    using value_search_policy = trlc::policy::BinarySearchPolicy;
    using name_search_policy = trlc::policy::CaseInsensitiveStringSearchPolicy;
    using unknown_policy = trlc::policy::UnknownPolicy;
    using enum_type = trlc::Enum<value_type, holder>;
    using iterator = trlc::EnumIterator<holder>;
};

TRLC_ENUM_DETAIL(Colors, CustomEnumDefine,
    RED,
    BLUE,
    GREEN)
Enter fullscreen mode Exit fullscreen mode

Check the repository for detailed.

Thanks for checking out trlc_enum! Happy coding, and I hope this library helps simplify your C++ projects.


Top comments (2)

Collapse
 
pgradot profile image
Pierre Gradot

Hello

I've tried a few minutes the library. I have seen interesting stuffs.

However, I believe your repository is missing at least 2 things:

  • an example of actual usage
  • a disclaimer about the size of an object

Using your example.cpp:

void do_something(Cars::enum_type r) {
  std::cout << "This is a car: " <<  r << " " << r.value() << '\n';
}

int main()
{
   do_something(Cars::SUV);
  static_assert(sizeof(Cars::enum_type) == 40);
}
Enter fullscreen mode Exit fullscreen mode

This is quite relevant I think.

Have you disable the copy constructor/operator of the enum type? Considered their size and supposing they are constexpr obejcts, it might be relevant.

Collapse
 
tranglecong profile image
Tráng Lê Công

@pgradot Thank you for reading and providing feedback on it.

I will add some more usage examples when I use this enum for my upcoming project.

The size of enumtype is essentially the size of a struct that includes value_type, std::string_view name, and tag. With the default trlc_enum, I'm using value_type: int64_t, so the total size will be: int64_t + std::string_view + std::string_view = 8 + 16 + 16 = 40

However, enums are constexpr, so they will be evaluated and replaced at compile time. This will reduce execution costs at runtime.

I didn’t disable the copy constructor/operator of the enum type because I want to be able to use it flexibly

#include <iostream>
#include <trlc/enum.hpp>

TRLC_ENUM(Color,
          RED,
          GREEN,
          BLUE)

int main()
{
    // The value of the original enum cannot be changed.
    // Color::RED = Color::GREEN;

    // We can use `Color::enum_type` just like a regular type.
    Color::enum_type a_color;
    a_color = Color::RED;
    std::cout << "Current color: " << a_color.dump() << std::endl;
    // Current color: {"tag": "Color, "value": 0, "name": "RED", "desc": ""}

    a_color = Color::GREEN;
    std::cout << "Current color: " << a_color.dump() << std::endl;
    // Current color: {"tag": "Color, "value": 1, "name": "GREEN", "desc": ""}
}

Enter fullscreen mode Exit fullscreen mode