summaryrefslogtreecommitdiff
path: root/lib/enumDetails.h
blob: dfae082ac8550dfbbbfbd0ac990f95fdf719cbf2 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#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'};
#pragma GCC diagnostic push
#ifdef __clang__
#	pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
#endif
	constexpr static auto raw_value {value};
#pragma GCC diagnostic pop
};

/// 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"
#ifdef __clang__
#	pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
#endif
		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...>)
	{
#pragma GCC diagnostic push
#ifdef __clang__
#	pragma GCC diagnostic ignored "-Wenum-constexpr-conversion"
#endif
		return std::array {EnumValueDetails<static_cast<E>(n)>::raw_value...};
#pragma GCC diagnostic pop
	}

	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);
	}
};