summaryrefslogtreecommitdiff
path: root/lib/enumDetails.hpp
blob: 7234c6d86a92ad45ef4be2b0e7db828296925ce5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#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...>)
	{
		return std::array {EnumValueDetails<static_cast<E>(n)>::valid...};
	}
	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[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);
	}
};