HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
enum_metadata.hpp
1// Copyright Take Vos 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../macros.hpp"
8#include "cast.hpp"
9#include "terminate.hpp"
10#include "exception.hpp"
11#include <cstddef>
12#include <type_traits>
13#include <array>
14#include <algorithm>
15#include <string_view>
16#include <stdexcept>
17#include <concepts>
18
19hi_export_module(hikogui.utility.enum_metadata);
20
21hi_warning_push();
22// C26445: Do not assign gsl::span or std::string_view to a reference. They are cheap to construct and are not owners of
23// the underlying data. (gsl.view).
24// False positive, sometimes the template is instantiated with string_view, sometimes not.
25hi_warning_ignore_msvc(26445);
26
27hi_export namespace hi { inline namespace v1 {
28
35template<typename ValueType, typename NameType, std::size_t N>
37public:
38 using value_type = ValueType;
39 using name_type = NameType;
40
43 constexpr static std::size_t count = N;
44
48
49 static_assert(std::is_enum_v<value_type>, "value_type Must be an enum");
50 static_assert(N != 0);
51
54 [[nodiscard]] constexpr size_t size() const noexcept
55 {
56 return count;
57 }
58
61 [[nodiscard]] constexpr value_type minimum() const noexcept
62 {
63 return std::get<0>(_by_value).value;
64 }
65
68 [[nodiscard]] constexpr value_type maximum() const noexcept
69 {
70 return std::get<N - 1>(_by_value).value;
71 }
72
86 template<typename... Args>
87 [[nodiscard]] constexpr enum_metadata(Args const&...args) noexcept
88 {
89 static_assert(sizeof...(Args) == N * 2);
90 add_value_name<0>(args...);
91
92 std::sort(_by_name.begin(), _by_name.end(), [](auto const& a, auto const& b) {
93 return a.name < b.name;
94 });
95
96 std::sort(_by_value.begin(), _by_value.end(), [](auto const& a, auto const& b) {
97 return std::to_underlying(a.value) < std::to_underlying(b.value);
98 });
99
100 values_are_continues = check_values_are_continues();
101 }
102
108 template<std::convertible_to<name_type> Name>
109 [[nodiscard]] constexpr bool contains(Name&& name) const noexcept
110 {
111 return find(name_type{std::forward<Name>(name)}) != nullptr;
112 }
113
119 [[nodiscard]] constexpr bool contains(value_type value) const noexcept
120 {
121 return find(value) != nullptr;
122 }
123
130 template<std::convertible_to<name_type> Name>
131 [[nodiscard]] constexpr value_type at(Name&& name) const
132 {
133 if (auto const *value = find(name_type{std::forward<Name>(name)})) {
134 return *value;
135 } else {
136 throw std::out_of_range{"enum_metadata::at"};
137 }
138 }
139
146 [[nodiscard]] constexpr name_type const& at(value_type value) const
147 {
148 if (auto const *name = find(value)) {
149 return *name;
150 } else {
151 throw std::out_of_range{"enum_metadata::at"};
152 }
153 }
154
160 template<std::convertible_to<name_type> Name>
161 [[nodiscard]] constexpr std::optional<value_type> at_if(Name&& name) const noexcept
162 {
163 if (auto const *value = find(name_type{std::forward<Name>(name)})) {
164 return *value;
165 } else {
166 return std::nullopt;
167 }
168 }
169
176 template<std::convertible_to<name_type> Name>
177 [[nodiscard]] constexpr value_type at(Name&& name, value_type default_value) const noexcept
178 {
179 if (auto const *value = find(name_type{std::forward<Name>(name)})) {
180 return *value;
181 } else {
182 return default_value;
183 }
184 }
185
192 template<std::convertible_to<name_type> Name>
193 [[nodiscard]] constexpr name_type at(value_type value, Name&& default_name) const noexcept
194 {
195 if (auto const *name = find(value)) {
196 return *name;
197 } else {
198 return std::forward<Name>(default_name);
199 }
200 }
201
208 template<std::convertible_to<name_type> Name>
209 [[nodiscard]] constexpr value_type operator[](Name&& name) const noexcept
210 {
211 auto *value = find(name_type{std::forward<Name>(name)});
212 hi_assert_not_null(value);
213 return *value;
214 }
215
222 [[nodiscard]] constexpr name_type const& operator[](value_type value) const noexcept
223 {
224 auto *name = find(value);
225 hi_assert_not_null(name);
226 return *name;
227 }
228
229private:
230 struct value_name {
231 value_type value;
232 name_type name;
233
234 constexpr value_name() noexcept : value(), name() {}
235 constexpr value_name(value_type value, name_type name) noexcept : value(value), name(std::move(name)) {}
236 };
237
240
241 [[nodiscard]] constexpr name_type const *find(value_type value) const noexcept
242 {
244 // If the enum values are continues we can do an associative lookup.
245 auto const it = _by_value.begin();
246 auto const offset = std::to_underlying(it->value);
247 auto const i = std::to_underlying(value) - offset;
248 return (i >= 0 and i < N) ? &(it + i)->name : nullptr;
249
250 } else {
251 auto const it = std::lower_bound(_by_value.begin(), _by_value.end(), value, [](auto const& item, auto const& key) {
252 return item.value < key;
253 });
254
255 return (it != _by_value.end() and it->value == value) ? &it->name : nullptr;
256 }
257 }
258
259 [[nodiscard]] constexpr value_type const *find(name_type const& name) const noexcept
260 {
261 auto const it = std::lower_bound(_by_name.begin(), _by_name.end(), name, [](auto const& item, auto const& key) {
262 return item.name < key;
263 });
264
265 return (it != _by_name.end() and it->name == name) ? &it->value : nullptr;
266 }
267
272 template<std::size_t I, typename... Rest>
273 constexpr void add_value_name(value_type value, name_type name, Rest const&...rest) noexcept
274 {
275 static_assert(sizeof...(Rest) % 2 == 0);
276
277 std::get<I>(_by_name) = {value, name};
278 std::get<I>(_by_value) = {value, std::move(name)};
279
280 if constexpr (sizeof...(Rest) > 0) {
281 add_value_name<I + 1>(rest...);
282 }
283 }
284
289 [[nodiscard]] constexpr bool check_values_are_continues() const noexcept
290 {
291 auto check_value = std::to_underlying(minimum());
292 for (auto const& item : _by_value) {
293 if (std::to_underlying(item.value) != check_value++) {
294 return false;
295 }
296 }
297 return true;
298 }
299};
300
301template<typename T>
303 using type = std::decay_t<T>;
304};
305
306// clang-format off
307template<> struct enum_metadata_name<char const *> { using type = std::string_view; };
308template<> struct enum_metadata_name<char *> { using type = std::string_view; };
309template<size_t N> struct enum_metadata_name<char [N]> { using type = std::string_view; };
310template<size_t N> struct enum_metadata_name<char const [N]> { using type = std::string_view; };
311// clang-format on
312
313template<typename T>
314using enum_metadata_name_t = enum_metadata_name<T>::type;
315
316template<typename ValueType, typename NameType, typename... Rest>
317enum_metadata(ValueType const&, NameType const&, Rest const&...)
318 -> enum_metadata<ValueType, enum_metadata_name_t<NameType>, (sizeof...(Rest) + 2) / 2>;
319
320}} // namespace hi::inline v1
321
322hi_warning_pop();
Functions for casting values between types savely.
Utilities for throwing exceptions and terminating the application.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
A object that holds enum-values and strings.
Definition enum_metadata.hpp:36
constexpr value_type maximum() const noexcept
Get the maximum value.
Definition enum_metadata.hpp:68
constexpr value_type at(Name &&name, value_type default_value) const noexcept
Get an enum-value from a name.
Definition enum_metadata.hpp:177
constexpr value_type minimum() const noexcept
Get the minimum value.
Definition enum_metadata.hpp:61
constexpr value_type at(Name &&name) const
Get an enum-value from a name.
Definition enum_metadata.hpp:131
constexpr bool contains(value_type value) const noexcept
Check if the enum has a value.
Definition enum_metadata.hpp:119
constexpr name_type at(value_type value, Name &&default_name) const noexcept
Get a name from an enum-value.
Definition enum_metadata.hpp:193
constexpr std::optional< value_type > at_if(Name &&name) const noexcept
Get an enum-value from a name.
Definition enum_metadata.hpp:161
constexpr bool contains(Name &&name) const noexcept
Check if the enum has a name.
Definition enum_metadata.hpp:109
constexpr value_type operator[](Name &&name) const noexcept
Get an enum-value from a name.
Definition enum_metadata.hpp:209
constexpr name_type const & at(value_type value) const
Get a name from an enum-value.
Definition enum_metadata.hpp:146
static constexpr std::size_t count
The number of enum values.
Definition enum_metadata.hpp:43
constexpr size_t size() const noexcept
Get the number of enum values.
Definition enum_metadata.hpp:54
constexpr enum_metadata(Args const &...args) noexcept
Construct a enum-names table object.
Definition enum_metadata.hpp:87
bool values_are_continues
The numeric values in the enum do not contain a gap.
Definition enum_metadata.hpp:47
constexpr name_type const & operator[](value_type value) const noexcept
Get a name from an enum-value.
Definition enum_metadata.hpp:222
Definition enum_metadata.hpp:302
T begin(T... args)
T end(T... args)
T lower_bound(T... args)
T move(T... args)
T sort(T... args)