Last active
April 2, 2018 09:32
-
-
Save Smertig/4088143f20d0eef7dcf9b648a4af3ac3 to your computer and use it in GitHub Desktop.
Simple enum traits implementation for C++17
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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