Skip to content

Commit 2a4e05f

Browse files
committed
Split tombstone_traits into two classes: tombstone, which users specialize, and tombstone_traits, which users call.
Add several into `tombstone_traits` that users implemented `tombstone` correctly. Rename `tombstone_traits_composer` to `tombstone_member`. Move `integer_tombstone_traits` specialization into main `tombstone` file.
1 parent 3d01bff commit 2a4e05f

13 files changed

+230
-211
lines changed

source/bounded/CMakeLists.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ target_sources(bounded PUBLIC
7070
hash.cpp
7171
homogeneous_equals.cpp
7272
integer.cpp
73-
integer_tombstone_traits.cpp
7473
integral.cpp
7574
integral_constant_of_integral.cpp
7675
is_bounded_integer.cpp
@@ -100,7 +99,7 @@ target_sources(bounded PUBLIC
10099
stream.cpp
101100
test_int.cpp
102101
to_integer.cpp
103-
tombstone_traits.cpp
102+
tombstone.cpp
104103
type.cpp
105104
unchecked.cpp
106105
underlying_type_t.cpp
@@ -132,7 +131,7 @@ target_sources(bounded_test PRIVATE
132131
test/conditional.cpp
133132
test/stream.cpp
134133
test/to_integer.cpp
135-
test/tombstone_traits.cpp
134+
test/tombstone.cpp
136135
)
137136

138137
target_link_libraries(bounded_test PUBLIC

source/bounded/bounded.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export import bounded.equality_comparable;
3232
export import bounded.function_ptr;
3333
export import bounded.hash;
3434
export import bounded.integer;
35-
export import bounded.integer_tombstone_traits;
3635
export import bounded.integral;
3736
export import bounded.isomorphic_to_integral;
3837
export import bounded.lazy_init;
@@ -54,7 +53,7 @@ export import bounded.size_of;
5453
export import bounded.std_iterator;
5554
export import bounded.stream;
5655
export import bounded.to_integer;
57-
export import bounded.tombstone_traits;
56+
export import bounded.tombstone;
5857
export import bounded.type;
5958
export import bounded.unchecked;
6059
export import bounded.unsigned_builtin;

source/bounded/integer_tombstone_traits.cpp renamed to source/bounded/test/tombstone.cpp

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,70 @@
1-
// Copyright David Stone 2020.
1+
// Copyright David Stone 2024.
22
// Distributed under the Boost Software License, Version 1.0.
33
// (See accompanying file LICENSE_1_0.txt or copy at
44
// http://www.boost.org/LICENSE_1_0.txt)
55

6-
module;
6+
export module bounded.test.tombstone;
77

8-
#include <bounded/conditional.hpp>
9-
10-
export module bounded.integer_tombstone_traits;
11-
12-
import bounded.arithmetic.operators;
13-
import bounded.bounded_integer;
8+
import bounded.arithmetic.unary_minus;
149
import bounded.comparison;
15-
import bounded.conditional_function;
1610
import bounded.integer;
1711
import bounded.literal;
18-
import bounded.tombstone_traits;
19-
import bounded.unchecked;
20-
21-
import numeric_traits;
12+
import bounded.tombstone;
2213

23-
import std_module;
14+
namespace {
15+
using namespace bounded::literal;
2416

25-
namespace bounded {
17+
struct a {
18+
int m;
19+
};
2620

27-
template<bounded_integer T>
28-
struct tombstone_traits<T> {
21+
struct b {
22+
constexpr b():
23+
m(1)
24+
{
25+
}
26+
a m;
2927
private:
30-
using underlying = typename T::underlying_type;
31-
static constexpr auto underlying_min = constant<numeric_traits::min_value<underlying>>;
32-
static constexpr auto underlying_max = constant<numeric_traits::max_value<underlying>>;
33-
static constexpr auto spare_below = numeric_traits::min_value<T> - underlying_min;
34-
static constexpr auto spare_above = underlying_max - numeric_traits::max_value<T>;
35-
public:
36-
static constexpr auto spare_representations = conditional_function<std::is_empty_v<T>>(
37-
constant<0>,
38-
spare_below + spare_above
39-
);
40-
41-
template<bounded_integer Index> requires(Index() < spare_representations)
42-
static constexpr auto make(Index const index) noexcept {
43-
auto const value = conditional_function<index < spare_below>(
44-
index + underlying_min,
45-
index - spare_below + numeric_traits::max_value<T> + constant<1>
46-
);
47-
return T(underlying(value), unchecked);
28+
constexpr explicit b(bounded::tombstone_tag, auto const make):
29+
m(make())
30+
{
31+
}
32+
friend bounded::tombstone_member<&b::m>;
33+
};
34+
35+
} // namespace
36+
37+
template<>
38+
struct bounded::tombstone<a> {
39+
static constexpr auto make(bounded::constant_t<0>) noexcept -> a {
40+
return a(0);
4841
}
49-
static constexpr auto index(T const & value) noexcept {
50-
auto const bounded_value = integer(value.m_value);
51-
auto const result =
52-
BOUNDED_CONDITIONAL(bounded_value < numeric_traits::min_value<T>, bounded_value - underlying_min,
53-
BOUNDED_CONDITIONAL(bounded_value > numeric_traits::max_value<T>, bounded_value + spare_below - numeric_traits::max_value<T> - constant<1>,
54-
constant<-1>
55-
));
56-
return bounded::assume_in_range(result, constant<-1>, spare_representations - constant<1>);
42+
static constexpr auto index(a const & value) noexcept -> bounded::integer<-1, 0> {
43+
if (value.m == 0) {
44+
return 0_bi;
45+
}
46+
return -1_bi;
5747
}
5848
};
5949

60-
} // namespace bounded
50+
template<>
51+
struct bounded::tombstone<b> : bounded::tombstone_member<&b::m> {
52+
};
53+
54+
namespace {
55+
56+
using a_traits = bounded::tombstone_traits<a>;
57+
static_assert(a_traits::spare_representations == 1_bi);
58+
static_assert(a_traits::make(0_bi).m == 0);
59+
static_assert(a_traits::index(a(0)) == 0_bi);
60+
static_assert(a_traits::index(a(1)) == -1_bi);
61+
62+
using b_traits = bounded::tombstone_traits<b>;
63+
static_assert(b_traits::spare_representations == 1_bi);
64+
static_assert(b_traits::make(0_bi).m.m == 0);
65+
static_assert(b_traits::index(b_traits::make(0_bi)) == 0_bi);
66+
static_assert(b_traits::index(b()) == -1_bi);
67+
6168

6269
using namespace bounded::literal;
6370

@@ -153,3 +160,5 @@ static_assert(reversible<bounded::integer<-50, 49>>(
153160
static_assert(bounded::tombstone_traits<bounded::integer<0, 255>>::spare_representations == 0_bi);
154161
static_assert(bounded::tombstone_traits<bounded::integer<-128, 127>>::spare_representations == 0_bi);
155162
static_assert(bounded::tombstone_traits<bounded::integer<53, 53>>::spare_representations == 0_bi);
163+
164+
} // namespace

source/bounded/test/tombstone_traits.cpp

Lines changed: 0 additions & 69 deletions
This file was deleted.

source/bounded/tombstone.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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

Comments
 (0)