summaryrefslogtreecommitdiff
path: root/lib/enumDetails.h
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-04-29 19:07:11 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2023-04-29 19:07:11 +0100
commit5a0b3927a33807cca4c77c40eb873f8a3b51b0b0 (patch)
tree4af0585ee8f8f468ab10c0a4fe9994fb30b79599 /lib/enumDetails.h
parentDunno how, but some DOS new lines got in here! (diff)
downloadilt-5a0b3927a33807cca4c77c40eb873f8a3b51b0b0.tar.bz2
ilt-5a0b3927a33807cca4c77c40eb873f8a3b51b0b0.tar.xz
ilt-5a0b3927a33807cca4c77c40eb873f8a3b51b0b0.zip
Drop .hpp for header only things
Half of them acquired a .cpp part anyway
Diffstat (limited to 'lib/enumDetails.h')
-rw-r--r--lib/enumDetails.h143
1 files changed, 143 insertions, 0 deletions
diff --git a/lib/enumDetails.h b/lib/enumDetails.h
new file mode 100644
index 0000000..5966be2
--- /dev/null
+++ b/lib/enumDetails.h
@@ -0,0 +1,143 @@
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <optional>
+#include <string_view>
+
+/// EnumDetailsBase
+// Shared helpers
+struct EnumDetailsBase {
+ template<size_t len>
+ constexpr static auto
+ strArr(auto input, auto start, auto end)
+ {
+ std::array<char, len> out;
+ input.copy(out.begin(), end - start, start);
+ return out;
+ }
+};
+
+/// EnumTypeDetails
+// Extracts the fully qualified name of the enumeration
+template<typename E> struct EnumTypeDetails : EnumDetailsBase {
+#ifndef ENUM_PROBE
+protected:
+#endif
+ constexpr static std::string_view SEARCH_TYPE {"E = "};
+ constexpr static auto
+ typeraw()
+ {
+ return std::string_view {__PRETTY_FUNCTION__};
+ };
+ constexpr static auto typeNameStart {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()};
+ constexpr static auto typeNameEnd {typeraw().find_first_of("];", typeNameStart)};
+ constexpr static auto typeNameLen {typeNameEnd - typeNameStart};
+ constexpr static auto typeNameArr {strArr<typeNameLen>(typeraw(), typeNameStart, typeNameEnd)};
+
+public:
+ constexpr static std::string_view typeName {typeNameArr.data(), typeNameArr.size()};
+};
+
+/// EnumValueDetails
+// Extracts the value name and constructs string_views of the parts
+template<auto value> struct EnumValueDetails : public EnumTypeDetails<decltype(value)> {
+#ifndef ENUM_PROBE
+private:
+#endif
+ using T = EnumTypeDetails<decltype(value)>;
+ constexpr static auto
+ raw()
+ {
+ return std::string_view {__PRETTY_FUNCTION__};
+ };
+ constexpr static auto nameStart {raw().find_last_of(": ") + 1};
+ constexpr static auto nameEnd {raw().find_first_of("];", nameStart)};
+ constexpr static auto nameLen {nameEnd - nameStart};
+ constexpr static auto nameArr {EnumValueDetails::template strArr<nameLen>(raw(), nameStart, nameEnd)};
+
+public:
+ constexpr static std::string_view valueName {nameArr.data(), nameArr.size()};
+ constexpr static auto valid {valueName.back() < '0' || valueName.back() > '9'};
+ constexpr static auto raw_value {value};
+};
+
+/// EnumValueCollection
+// Customisation point for specifying the range of underlying values your enum can have
+template<typename E> struct EnumValueCollection {
+ using Vs = std::make_integer_sequence<int, 256>;
+};
+
+/// EnumDetails
+// Interface for lookups/checks/etc at runtime
+template<typename E> struct EnumDetails {
+#ifndef ENUM_PROBE
+private:
+#endif
+ template<auto... n>
+ constexpr static auto
+ get_valids(std::integer_sequence<int, n...>)
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+ return std::array {EnumValueDetails<static_cast<E>(n)>::valid...};
+#pragma GCC diagnostic pop
+ }
+ template<auto... n>
+ constexpr static auto
+ get_values(std::integer_sequence<int, n...>)
+ {
+ return std::array {EnumValueDetails<static_cast<E>(n)>::raw_value...};
+ }
+ template<auto... n>
+ constexpr static auto
+ get_valueNames(std::integer_sequence<int, n...>)
+ {
+ return std::array {EnumValueDetails<values[n]>::valueName...};
+ }
+
+ using EVC = EnumValueCollection<E>;
+ constexpr static auto valid_flags {get_valids(typename EVC::Vs {})};
+ constexpr static auto valid_count {std::count_if(valid_flags.begin(), valid_flags.end(), std::identity {})};
+
+ constexpr static auto
+ lookup(const auto key, const auto & search, const auto & out)
+ -> std::optional<typename std::decay_t<decltype(out)>::value_type>
+ {
+ if (const auto itr = std::find(search.begin(), search.end(), key); itr != search.end()) {
+ return out[static_cast<std::size_t>(std::distance(search.begin(), itr))];
+ }
+ return std::nullopt;
+ }
+
+public:
+ constexpr static auto values {[]() {
+ constexpr auto values {get_values(typename EVC::Vs {})};
+ static_assert(std::is_sorted(values.begin(), values.end()), "Candidate values must be sorted");
+ std::array<E, valid_count> out;
+ std::copy_if(values.begin(), values.end(), out.begin(), [valid = valid_flags.begin()](auto) mutable {
+ return *valid++;
+ });
+ return out;
+ }()};
+ constexpr static auto names {get_valueNames(std::make_integer_sequence<int, valid_count> {})};
+
+ constexpr static bool
+ is_valid(E value) noexcept
+ {
+ return std::binary_search(values.begin(), values.end(), value);
+ }
+
+ constexpr static std::optional<E>
+ parse(std::string_view name) noexcept
+ {
+ return lookup(name, names, values);
+ }
+
+ constexpr static std::optional<std::string_view>
+ to_string(E value) noexcept
+ {
+ return lookup(value, values, names);
+ }
+};