Skip to content

lackhole/meta_enum

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Meta Enum C++


Header-only C++17 library that provides static reflection of arbitrary meta-objects associated with enums, with minimum macro boilerplate.


* meta_enum ships two separate headers, each optimized for C++17 and C++20. See include/meta_enum for details.

Dependencies & Requirements

Example

Map arbitrary types to enum values

#include <string_view>
#include "meta_enum/meta_enum.hpp"

META_ENUM(MimeType, std::string_view,
    ((ApplicationJson, "application/json"))
    ((TextHtml, "text/html"))
    ((TextPlain, "text/plain"))
)

static_assert(std::same_as<meta_enum::underlying_meta_type_t<MimeType>, std::string_view>);
static_assert(MimeType::ApplicationJson == "application/json");

constexpr std::string_view type = "text/html";
static_assert(meta_enum::meta_to_enum<MimeType>(type) == MimeType::TextHtml);

switch (meta_enum::meta_to_enum(type)) {
    case MimeType::ApplicationJson:
    case MimeType::TextHtml:
    case MimeType::TextPlain:
}

Advanced usage

#include <string_view>
#include "meta_enum/meta_enum.hpp"

struct MetaObject {
    std::string_view desc;
    // ...
};

META_ENUM_CLASS(StatusCode, int, MetaObject,
    (((OK, 200), MetaObject{.desc = "OK"}))
    (((NotFound, 404), MetaObject{.desc = "Not Found"}))
    (((InternalServerError, 500), MetaObject{.desc = "Internal Server Error"}))
)

template <>
struct magic_enum::customize::enum_range<StatusCode> {
    static constexpr int min = 0;
    static constexpr int max = 999;
};

void handle(StatusCode code) {
  std::println("{}: {}", std::to_underlying(code), meta_enum::get_meta_value(code)->desc);
  switch (code) {
  case StatusCode::OK:
      // ...
  }
}

Usage

  • META_ENUM defines an unscoped enum, where comparison with the underlying meta type is allowed by default.
  • META_ENUM_CLASS defines a scoped enum, where comparison with the underlying meta type is not allowed by default. However, you can enable it by using the meta_enum::rel_ops namespace.
META_ENUM(ErrorCode, std::string_view,
    ((Value1, "value1"))
    ((Value2, "value2"))
)

META_ENUM_CLASS(MyEnum2, std::string_view,
    ((Value1, "value1"))
    ((Value2, "value2"))
)

int main() {
    MyEnum1::Value1 == "value1"; // true
    MyEnum2::Value1 == "value1"; // won't compile
    {
        using namespace meta_enum::rel_ops;
        MyEnum2::Value1 == "value1"; // true
    }
}

All features

META_ENUM, META_ENUM_CLASS
* Put extra parentheses around the enum value to specify the underlying value.

META_ENUM(MyEnum, std::string_view,
    ((Value1, "value1"))
    ((Value2, "value2"))
) // same as enum MyEnum { Value1, Value2, };

META_ENUM(MyEnum, std::string_view,
    (((Value1, 100), "value1"))
    ((Value2, "value2"))
) // same as enum MyEnum { Value1 = 100, Value2, };

META_ENUM(MyEnum, int, std::string_view,
    (((Value1, 100), "value1"))
    ((Value2, "value2"))
) // same as enum MyEnum : int { Value1 = 100, Value2, };

META_ENUM_ENABLE_ENUM, META_ENUM_ENABLE_ENUM_CLASS
Lets you to set non-meta_enum to meta_enums.

namespace third_party {
// Enum you can not modify
enum ThirdPartyEnum {
    Value1,
    Value2,
    Value3
};
} // namespace third_party

META_ENUM_ENABLE_ENUM(third_party::ThirdPartyEnum, ThirdPartyEnum, MyMetaObject,
    ((Value1, MyMetaObject{/* ... */}))
    ((Value2, MyMetaObject{/* ... */}))
    ((Value3, MyMetaObject{/* ... */}))
)

underlying_meta_type_t

static_assert(std::same_as<meta_enum::underlying_meta_type_t<MimeType>, std::string_view>);

get_meta_value

static_assert(meta_enum::get_meta_value<MimeType::TextHtml>() == "text/html");
static_assert(meta_enum::get_meta_value(MimeType::TextHtml) == "text/html"); // returns std::optional
static_assert(meta_enum::get_meta_value(static_cast<MimeType>(999)).has_value() == false); // returns std::optional

get_meta_reference

META_ENUM(BigMetaEnum, BigOrNonCopyableMetaObject,
    ((Value1, BigOrNonCopyableMetaObject{/* ... */}))
)

static_assert(meta_enum::get_meta_reference<BigMetaEnum::Value1>() == /* ... */);
static_assert(meta_enum::get_meta_reference(BigMetaEnum::Value1) == /* ... */); // returns std::optional

meta_equal_to
transparent comparison object

static_assert(meta_enum::meta_equal_to{}(MimeType::TextHtml, "text/html"));

META_ENUM_TRAITS_NAMESPACE, META_ENUM_NO_TRAITS_NAMESPACE
See customization

Notes

Underlying type

The library does not block declaration of meta types as the same type as the underlying type, but it is recommended to avoid doing so to prevent confusion.

META_ENUM(IntEnum, int, // not recommended
    ((Value1, 100)) // The underlying value is 1 but the meta value is 100, which is confusing.
    ((Value2, 200))
)

static_assert(IntEnum::Value1 == 0);
static_assert(IntEnum::Value2 == 1);

ADL

meta_enum uses ADL to lookup if the given enum is meta_enum or not. You may put META_ENUM and META_ENUM_CLASS wherever you want.
The macro only encapsulates declaration of meta values.

namespace test {

META_ENUM(TestStatus, std::string_view,
    ((idle, "IDLE"sv))
    ((running, "RUNNING"sv))
    ((done, "DONE"sv))
)

} // namespace test

static_assert(meta_enum::is_meta_enum_v<test::TestStatus>);
static_assert(meta_enum::get_meta_value<test::TestStatus::idle>() == "IDLE"sv);
static_assert(meta_enum::get_meta_value(test::TestStatus::running) == "RUNNING"sv);

Customization

meta_enum declares meta values in a separate traits class inside META_ENUM_TRAITS_NAMESPACE namespace from where it is declared. You can define META_ENUM_TRAITS_NAMESPACE before including the header to change the namespace where the traits class is declared. Or you can define META_ENUM_NO_TRAITS_NAMESPACE to declare the traits class in the same namespace as the macro is used.

#define META_ENUM_NO_TRAITS_NAMESPACE

#include "meta_enum/meta_enum.hpp"

About

Static reflection of arbitrary meta-objects associated with enums

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors