1
+ // Copyright David Stone 2020.
2
+ // Distributed under the Boost Software License, Version 1.0.
3
+ // (See accompanying file LICENSE_1_0.txt or copy at
4
+ // http://www.boost.org/LICENSE_1_0.txt)
5
+
6
+ module ;
7
+
8
+ #include < bounded/conditional.hpp>
9
+
10
+ export module bounded.tombstone;
11
+
12
+ import bounded.arithmetic.operators;
13
+ import bounded.bounded_integer;
14
+ import bounded.comparison;
15
+ import bounded.conditional_function;
16
+ import bounded.declval;
17
+ import bounded.integer;
18
+ import bounded.literal;
19
+ import bounded.normalize;
20
+ import bounded.unchecked;
21
+
22
+ import numeric_traits;
23
+ import std_module;
24
+
25
+ namespace bounded {
26
+ using namespace bounded ::literal;
27
+
28
+ // Users should specialize `bounded::tombstone` for their types. The values
29
+ // should be accessed through `bounded::tombstone_traits`.
30
+
31
+ // An alternative design would be to implement this in terms of
32
+ // std::array<std::byte, sizeof(T)>. That approach is more correct because it
33
+ // does not claim that there is a T object somewhere, but it would not work with
34
+ // constexpr. This means all of the users of tombstone_traits would have to have
35
+ // a separate code path guarded by `if consteval` to avoid the small object
36
+ // optimization somehow.
37
+
38
+ template <typename >
39
+ constexpr auto is_constant = false ;
40
+
41
+ template <auto value>
42
+ constexpr auto is_constant<integer<value, value>> = true ;
43
+
44
+ template <typename T>
45
+ concept constant_integer = is_constant<T>;
46
+
47
+ export template <typename T>
48
+ struct tombstone {
49
+ // `index` is a constant in the range `[0, max result of index(value)]`.
50
+ static auto make (auto index) noexcept -> T = delete;
51
+ // Returns -1_bi if there is an object present
52
+ static constexpr auto index (T const &) noexcept -> constant_t<-1> {
53
+ return -1_bi;
54
+ }
55
+ };
56
+
57
+ template <bounded_integer T> requires (!constant_integer<T>)
58
+ struct tombstone<T> {
59
+ private:
60
+ using underlying = typename T::underlying_type;
61
+ static constexpr auto underlying_min = constant<numeric_traits::min_value<underlying>>;
62
+ static constexpr auto underlying_max = constant<numeric_traits::max_value<underlying>>;
63
+ static constexpr auto spare_below = numeric_traits::min_value<T> - underlying_min;
64
+ static constexpr auto spare_above = underlying_max - numeric_traits::max_value<T>;
65
+ using index_t = bounded::integer<
66
+ -1 ,
67
+ normalize<spare_below + spare_above - 1_bi>
68
+ >;
69
+ public:
70
+ static constexpr auto make (constant_integer auto const index) noexcept -> T {
71
+ auto const value = conditional_function<index < spare_below>(
72
+ index + underlying_min,
73
+ index - spare_below + numeric_traits::max_value<T> + 1_bi
74
+ );
75
+ return T (underlying (value), unchecked);
76
+ }
77
+ static constexpr auto index (T const & value) noexcept -> index_t {
78
+ auto const bounded_value = integer (value.m_value );
79
+ auto const result =
80
+ BOUNDED_CONDITIONAL (bounded_value < numeric_traits::min_value<T>, bounded_value - underlying_min,
81
+ BOUNDED_CONDITIONAL (bounded_value > numeric_traits::max_value<T>, bounded_value + spare_below - numeric_traits::max_value<T> - 1_bi,
82
+ -1_bi
83
+ ));
84
+ return bounded::assume_in_range<index_t >(result);
85
+ }
86
+ };
87
+
88
+ template <typename T>
89
+ constexpr auto max_tombstone_index =
90
+ numeric_traits::max_value<decltype (tombstone<T>::index(declval<T const &>()))>;
91
+
92
+ template <typename T>
93
+ concept make_accepts_min_and_max = requires {
94
+ { tombstone<T>::make (0_bi) } -> std::same_as<T>;
95
+ { tombstone<T>::make (max_tombstone_index<T>) } -> std::same_as<T>;
96
+ };
97
+
98
+ template <typename Index, typename T>
99
+ concept tombstone_index =
100
+ constant_integer<Index> and
101
+ 0_bi <= Index() and Index () <= max_tombstone_index<T>;
102
+
103
+ export template <typename T>
104
+ struct tombstone_traits {
105
+ using index_t = decltype (tombstone<T>::index(declval<T>()));
106
+
107
+ static_assert (numeric_traits::min_value<index_t > == -1_bi);
108
+
109
+ static constexpr auto spare_representations = numeric_traits::max_value<index_t > + 1_bi;
110
+
111
+ static_assert (spare_representations == 0_bi or make_accepts_min_and_max<T>);
112
+
113
+ static constexpr auto make (tombstone_index<T> auto const index) noexcept -> T {
114
+ static_assert (noexcept (tombstone<T>::make (index)));
115
+ return tombstone<T>::make (index);
116
+ }
117
+ // Returns -1_bi if there is an object present
118
+ static constexpr auto index (T const & value) noexcept -> index_t {
119
+ static_assert (noexcept (tombstone<T>::index (value)));
120
+ return tombstone<T>::index (value);
121
+ }
122
+ };
123
+
124
+ export struct tombstone_tag {};
125
+
126
+ export template <auto >
127
+ struct tombstone_member ;
128
+
129
+ template <typename Outer, typename Inner, Inner Outer::* pointer>
130
+ struct tombstone_member <pointer> {
131
+ static constexpr auto make (auto const index) noexcept -> Outer {
132
+ return Outer (
133
+ tombstone_tag (),
134
+ [=] noexcept { return tombstone<Inner>::make (index); }
135
+ );
136
+ }
137
+ static constexpr auto index (Outer const & value) noexcept {
138
+ return tombstone<Inner>::index (value.*pointer);
139
+ }
140
+ };
141
+
142
+ } // namespace bounded
0 commit comments