Skip to content

Instantly share code, notes, and snippets.

@Smertig
Last active April 2, 2018 09:32
Show Gist options
  • Save Smertig/4088143f20d0eef7dcf9b648a4af3ac3 to your computer and use it in GitHub Desktop.
Save Smertig/4088143f20d0eef7dcf9b648a4af3ac3 to your computer and use it in GitHub Desktop.
Simple enum traits implementation for C++17
//
// Created by int3 on 14.02.18.
//
/*
Usage:
enum class Foo : int8_t {
a, b, c
};
ENUM_TRAIT(Foo) {
{ Foo::a, "a" },
{ Foo::b, "b" },
{ Foo::c, "c" }
};
enum_traits<Foo>::to_value(Foo::a) == 0;
enum_traits<Foo>::enums() == { Foo::a, Foo::b, Foo::c };
enum_traits<Foo>::named_enums() == { { Foo::a, "a" }, { Foo::b, "b" }, { Foo::c, "c" } };
enum_traits<Foo>::values() == { 0, 1, 2 };
enum_traits<Foo>::strings() == { "a", "b", "c" };
enum_traits<Foo>::is_valid(Foo::a) == true;
enum_traits<Foo>::is_valid("qwerty") == false;
enum_traits<Foo>::to_string(Foo::a) == "a";
enum_traits<Foo>::from_string("a") == Foo::a;
and {to|from}_string nothrow versions
*/
#pragma once
#include <string_view>
#include <algorithm>
template <class Enum>
[[maybe_unused]] constexpr std::initializer_list<std::pair<Enum, const char*>> enum_trait{};
template <class Enum>
class enum_traits {
static_assert(std::is_enum<Enum>::value, "enum_traits is only available for enums");
static constexpr const std::initializer_list<std::pair<Enum, const char*>>& get_trait() {
return enum_trait<Enum>;
}
using RealType = std::underlying_type_t<Enum>;
static constexpr const auto Size = get_trait().size();
using values_array_t = std::array<RealType, Size>;
using enums_array_t = std::array<Enum, Size>;
using strings_array_t = std::array<std::string_view, Size>;
using pair_t = std::pair<RealType, std::string_view>;
using pairs_array_t = std::array<pair_t, Size>;
using named_enum_t = std::pair<Enum, std::string_view>;
using named_enums_array_t = std::array<named_enum_t, Size>;
static const pairs_array_t& get_pairs() noexcept {
static const auto ret = []{
pairs_array_t ret;
int i = 0;
for (auto& p : get_trait()) {
ret[i++] = { static_cast<RealType>(p.first), std::string_view(p.second) };
}
return ret;
}();
return ret;
};
template <class T, T pair_t::*field>
static const pairs_array_t& get_sorted_by() noexcept {
static const auto sorted = []{
pairs_array_t ret = get_pairs();
std::sort(ret.begin(), ret.end(), [](const auto& l, const auto& r) {
return l.*field < r.*field;
});
return ret;
}();
return sorted;
};
template <class T, T pair_t::* field>
static const pair_t* _find(T value) noexcept {
auto& sorted = get_sorted_by<T, field>();
auto it = std::lower_bound(sorted.begin(), sorted.end(), value, [](const auto& l, const auto& r) {
return l.*field < r;
});
if (it != sorted.end() && !(value < it->*field)) {
return &*it;
}
else {
return nullptr;
}
};
public:
static constexpr const auto size = Size;
static RealType to_value(Enum e) noexcept {
return static_cast<RealType>(e);
}
static const enums_array_t& enums() noexcept {
static const auto enums = []{
enums_array_t ret;
int i = 0;
for (auto& p : get_trait()) {
ret[i++] = p.first;
}
return ret;
}();
return enums;
};
static const named_enums_array_t& named_enums() noexcept {
static const auto named_enums = []{
named_enums_array_t ret;
int i = 0;
for (auto& p : get_trait()) {
ret[i++] = { p.first, p.second };
}
return ret;
}();
return named_enums;
}
static const values_array_t& values() noexcept {
static const auto values = []{
values_array_t ret;
int i = 0;
for (auto& p : get_trait()) {
ret[i++] = static_cast<RealType>(p.first);
}
return ret;
}();
return values;
};
static const strings_array_t& strings() noexcept {
static const auto strings = []{
strings_array_t ret;
int i = 0;
for (auto& p : get_trait()) {
ret[i++] = p.second;
}
return ret;
}();
return strings;
}
static bool is_valid(Enum e) noexcept {
return _find<RealType, &pair_t::first>(to_value(e)) != nullptr;
}
static bool is_valid(std::string_view str) noexcept {
return _find<std::string_view, &pair_t::second>(str) != nullptr;
}
static bool to_string(Enum e, std::string_view& str) noexcept {
auto p = _find<RealType, &pair_t::first>(to_value(e));
if (!p) {
return false;
}
else {
str = p->second;
return true;
}
}
static std::string_view to_string(Enum e) {
std::string_view ret;
if (!to_string(e, ret)) {
throw std::invalid_argument(u8"invalid enum value");
}
return ret;
}
static bool from_string(std::string_view str, Enum& e) noexcept {
auto p = _find<std::string_view, &pair_t::second>(str);
if (!p) {
return false;
}
else {
e = static_cast<Enum>(p->first);
return true;
}
}
static Enum from_string(std::string_view str) {
Enum e{};
if (!from_string(str, e)) {
throw std::invalid_argument(u8"invalid enum string");
}
return e;
}
};
#define ENUM_TRAIT(E) template <> [[maybe_unused]] constexpr std::initializer_list<std::pair<E, const char*>> enum_trait<E> =
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment