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.
- Magic Enum
- Boost.Preprocessor
- C++17 or later
#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:
}#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:
// ...
}
}META_ENUMdefines an unscoped enum, where comparison with the underlying meta type is allowed by default.META_ENUM_CLASSdefines a scoped enum, where comparison with the underlying meta type is not allowed by default. However, you can enable it by using themeta_enum::rel_opsnamespace.
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
}
}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::optionalget_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::optionalmeta_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
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);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);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"