Skip to content

Commit 1a68fae

Browse files
committed
🎨 Add ct_string deduction guide for {atomic_}bitset
1 parent e00f89a commit 1a68fae

File tree

4 files changed

+107
-73
lines changed

4 files changed

+107
-73
lines changed

include/stdx/atomic_bitset.hpp

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stdx/bitset.hpp>
55
#include <stdx/compiler.hpp>
66
#include <stdx/concepts.hpp>
7+
#include <stdx/ct_string.hpp>
78
#include <stdx/detail/bitset_common.hpp>
89
#include <stdx/type_traits.hpp>
910
#include <stdx/udls.hpp>
@@ -17,49 +18,52 @@
1718

1819
namespace stdx {
1920
inline namespace v1 {
20-
namespace detail {
21-
template <std::size_t N, typename StorageElem> class atomic_bitset {
22-
constexpr static auto bit = StorageElem{1U};
21+
template <auto Size, typename StorageElem =
22+
decltype(smallest_uint<to_underlying(Size), void>())>
23+
class atomic_bitset {
24+
constexpr static std::size_t N = to_underlying(Size);
25+
using elem_t = StorageElem;
2326

24-
static_assert(N <= std::numeric_limits<StorageElem>::digits,
27+
constexpr static auto bit = elem_t{1U};
28+
29+
static_assert(N <= std::numeric_limits<elem_t>::digits,
2530
"atomic_bitset is limited to a single storage element");
26-
static_assert(std::atomic<StorageElem>::is_always_lock_free,
27-
"atomic_bitset must always be lock free");
28-
std::atomic<StorageElem> storage{};
31+
std::atomic<elem_t> storage{};
2932

30-
constexpr static auto mask = bit_mask<StorageElem, N - 1>();
31-
StorageElem salient_value(std::memory_order order) const {
33+
constexpr static auto mask = bit_mask<elem_t, N - 1>();
34+
elem_t salient_value(std::memory_order order) const {
3235
return storage.load(order) & mask;
3336
}
3437

35-
[[nodiscard]] static constexpr auto
36-
value_from_string(std::string_view str, std::size_t pos, std::size_t n,
37-
char one) -> StorageElem {
38-
StorageElem ret{};
38+
[[nodiscard]] static constexpr auto value_from_string(std::string_view str,
39+
std::size_t pos,
40+
std::size_t n,
41+
char one) -> elem_t {
42+
elem_t ret{};
3943
auto const len = std::min(n, str.size() - pos);
4044
auto const s = str.substr(pos, std::min(len, N));
4145
auto i = bit;
4246
for (auto it = std::rbegin(s); it != std::rend(s); ++it) {
4347
if (*it == one) {
4448
ret |= i;
4549
}
46-
i = static_cast<StorageElem>(i << 1u);
50+
i = static_cast<elem_t>(i << 1u);
4751
}
4852
return ret;
4953
}
5054

51-
using bitset_t = bitset<N, StorageElem>;
55+
using bitset_t = bitset<Size, elem_t>;
5256

5357
public:
5458
constexpr atomic_bitset() = default;
5559
constexpr explicit atomic_bitset(std::uint64_t value)
56-
: storage{static_cast<StorageElem>(value & mask)} {}
60+
: storage{static_cast<elem_t>(value & mask)} {}
5761

5862
template <typename... Bs>
5963
constexpr explicit atomic_bitset(place_bits_t, Bs... bs)
60-
: storage{static_cast<StorageElem>(
61-
(StorageElem{} | ... |
62-
static_cast<StorageElem>(bit << to_underlying(bs))))} {}
64+
: storage{static_cast<elem_t>(
65+
(elem_t{} | ... |
66+
static_cast<elem_t>(bit << to_underlying(bs))))} {}
6367

6468
constexpr explicit atomic_bitset(all_bits_t) : storage{mask} {}
6569

@@ -68,6 +72,11 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
6872
char one = '1')
6973
: storage{value_from_string(str, pos, n, one)} {}
7074

75+
#if __cplusplus >= 202002L
76+
constexpr explicit atomic_bitset(ct_string<N + 1> s)
77+
: atomic_bitset{static_cast<std::string_view>(s)} {}
78+
#endif
79+
7180
template <typename T>
7281
[[nodiscard]] auto
7382
to(std::memory_order order = std::memory_order_seq_cst) const -> T {
@@ -93,13 +102,13 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
93102
}
94103
auto store(bitset_t b,
95104
std::memory_order order = std::memory_order_seq_cst) {
96-
storage.store(b.template to<StorageElem>(), order);
105+
storage.store(b.template to<elem_t>(), order);
97106
}
98107

99108
constexpr static std::integral_constant<std::size_t, N> size{};
100109

101110
constexpr static std::bool_constant<
102-
std::atomic<StorageElem>::is_always_lock_free>
111+
std::atomic<elem_t>::is_always_lock_free>
103112
is_always_lock_free{};
104113

105114
template <typename T> [[nodiscard]] auto operator[](T idx) const -> bool {
@@ -113,17 +122,17 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
113122
auto const pos = static_cast<std::size_t>(to_underlying(idx));
114123
if (value) {
115124
return bitset_t{
116-
storage.fetch_or(static_cast<StorageElem>(bit << pos), order)};
125+
storage.fetch_or(static_cast<elem_t>(bit << pos), order)};
117126
}
118127
return bitset_t{
119-
storage.fetch_and(static_cast<StorageElem>(~(bit << pos)), order)};
128+
storage.fetch_and(static_cast<elem_t>(~(bit << pos)), order)};
120129
}
121130

122131
auto set(lsb_t lsb, msb_t msb, bool value = true,
123132
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
124133
auto const l = to_underlying(lsb);
125134
auto const m = to_underlying(msb);
126-
auto const shifted_value = bit_mask<StorageElem>(m, l);
135+
auto const shifted_value = bit_mask<elem_t>(m, l);
127136
if (value) {
128137
return bitset_t{storage.fetch_or(shifted_value, order)};
129138
}
@@ -145,13 +154,12 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
145154

146155
template <typename T> auto reset(T idx) -> bitset_t {
147156
auto const pos = static_cast<std::size_t>(to_underlying(idx));
148-
return bitset_t{
149-
storage.fetch_and(static_cast<StorageElem>(~(bit << pos)))};
157+
return bitset_t{storage.fetch_and(static_cast<elem_t>(~(bit << pos)))};
150158
}
151159

152160
auto reset(std::memory_order order = std::memory_order_seq_cst)
153161
LIFETIMEBOUND -> atomic_bitset & {
154-
storage.store(StorageElem{}, order);
162+
storage.store(elem_t{}, order);
155163
return *this;
156164
}
157165

@@ -172,7 +180,7 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
172180
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
173181
auto const pos = static_cast<std::size_t>(to_underlying(idx));
174182
return bitset_t{
175-
storage.fetch_xor(static_cast<StorageElem>(bit << pos), order)};
183+
storage.fetch_xor(static_cast<elem_t>(bit << pos), order)};
176184
}
177185

178186
auto flip(std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
@@ -198,10 +206,9 @@ template <std::size_t N, typename StorageElem> class atomic_bitset {
198206
return static_cast<std::size_t>(popcount(salient_value(order)));
199207
}
200208
};
201-
} // namespace detail
202209

203-
template <auto N, typename StorageElem = void>
204-
using atomic_bitset = detail::atomic_bitset<
205-
to_underlying(N), decltype(smallest_uint<to_underlying(N), StorageElem>())>;
210+
#if __cplusplus >= 202002L
211+
template <std::size_t N> atomic_bitset(ct_string<N>) -> atomic_bitset<N - 1>;
212+
#endif
206213
} // namespace v1
207214
} // namespace stdx

include/stdx/bitset.hpp

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <stdx/bit.hpp>
44
#include <stdx/compiler.hpp>
55
#include <stdx/concepts.hpp>
6+
#include <stdx/ct_string.hpp>
67
#include <stdx/detail/bitset_common.hpp>
78
#include <stdx/type_traits.hpp>
89
#include <stdx/udls.hpp>
@@ -18,25 +19,29 @@
1819

1920
namespace stdx {
2021
inline namespace v1 {
21-
namespace detail {
22-
template <std::size_t N, typename StorageElem> class bitset {
22+
template <auto Size, typename StorageElem =
23+
decltype(smallest_uint<to_underlying(Size), void>())>
24+
class bitset {
25+
constexpr static std::size_t N = to_underlying(Size);
26+
using elem_t = StorageElem;
27+
2328
constexpr static auto storage_elem_size =
24-
std::numeric_limits<StorageElem>::digits;
29+
std::numeric_limits<elem_t>::digits;
2530
constexpr static auto storage_size =
2631
(N + storage_elem_size - 1) / storage_elem_size;
27-
constexpr static auto bit = StorageElem{1U};
28-
constexpr static auto allbits = std::numeric_limits<StorageElem>::max();
32+
constexpr static auto bit = elem_t{1U};
33+
constexpr static auto allbits = std::numeric_limits<elem_t>::max();
2934

30-
std::array<StorageElem, storage_size> storage{};
35+
std::array<elem_t, storage_size> storage{};
3136

32-
constexpr static auto lastmask = []() -> StorageElem {
37+
constexpr static auto lastmask = []() -> elem_t {
3338
if constexpr (N % storage_elem_size != 0) {
3439
return allbits >> (storage_elem_size - N % storage_elem_size);
3540
} else {
3641
return allbits;
3742
}
3843
}();
39-
constexpr auto highbits() const -> StorageElem {
44+
constexpr auto highbits() const -> elem_t {
4045
return storage.back() & lastmask;
4146
}
4247

@@ -99,21 +104,21 @@ template <std::size_t N, typename StorageElem> class bitset {
99104
for (auto e : storage) {
100105
while (e != 0) {
101106
auto const offset = static_cast<std::size_t>(countr_zero(e));
102-
e &= static_cast<StorageElem>(~(bit << offset));
107+
e &= static_cast<elem_t>(~(bit << offset));
103108
f(i + offset);
104109
}
105-
i += std::numeric_limits<StorageElem>::digits;
110+
i += std::numeric_limits<elem_t>::digits;
106111
}
107112
return std::forward<F>(f);
108113
}
109114

110-
template <typename F, std::size_t M, typename... S>
115+
template <typename F, auto M, typename... S>
111116
friend constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F;
112117

113118
public:
114119
constexpr bitset() = default;
115120
constexpr explicit bitset(std::uint64_t value) {
116-
if constexpr (std::is_same_v<StorageElem, std::uint64_t>) {
121+
if constexpr (std::is_same_v<elem_t, std::uint64_t>) {
117122
storage[0] = value;
118123
} else {
119124
for (auto &elem : storage) {
@@ -151,14 +156,19 @@ template <std::size_t N, typename StorageElem> class bitset {
151156
}
152157
}
153158

159+
#if __cplusplus >= 202002L
160+
constexpr explicit bitset(ct_string<N + 1> s)
161+
: bitset{static_cast<std::string_view>(s)} {}
162+
#endif
163+
154164
template <typename T> [[nodiscard]] constexpr auto to() const -> T {
155165
using U = underlying_type_t<T>;
156166
static_assert(
157167
unsigned_integral<U>,
158168
"Conversion must be to an unsigned integral type or enum!");
159169
static_assert(N <= std::numeric_limits<U>::digits,
160170
"Bitset too big for conversion to T");
161-
if constexpr (std::is_same_v<StorageElem, U>) {
171+
if constexpr (std::is_same_v<elem_t, U>) {
162172
return static_cast<T>(storage[0] & lastmask);
163173
} else {
164174
U result{highbits()};
@@ -191,9 +201,9 @@ template <std::size_t N, typename StorageElem> class bitset {
191201
auto const pos = static_cast<std::size_t>(to_underlying(idx));
192202
auto const [index, offset] = indices(pos);
193203
if (value) {
194-
storage[index] |= static_cast<StorageElem>(bit << offset);
204+
storage[index] |= static_cast<elem_t>(bit << offset);
195205
} else {
196-
storage[index] &= static_cast<StorageElem>(~(bit << offset));
206+
storage[index] &= static_cast<elem_t>(~(bit << offset));
197207
}
198208
return *this;
199209
}
@@ -205,25 +215,25 @@ template <std::size_t N, typename StorageElem> class bitset {
205215
auto [l_index, l_offset] = indices(l);
206216
auto const [m_index, m_offset] = indices(m);
207217

208-
using setfn = auto (*)(StorageElem *, StorageElem)->void;
218+
using setfn = auto (*)(elem_t *, elem_t)->void;
209219
auto const fn = [&]() -> setfn {
210220
if (value) {
211-
return [](StorageElem *ptr, StorageElem val) { *ptr |= val; };
221+
return [](elem_t *ptr, elem_t val) { *ptr |= val; };
212222
}
213-
return [](StorageElem *ptr, StorageElem val) { *ptr &= ~val; };
223+
return [](elem_t *ptr, elem_t val) { *ptr &= ~val; };
214224
}();
215225

216-
auto l_mask = std::numeric_limits<StorageElem>::max() << l_offset;
226+
auto l_mask = std::numeric_limits<elem_t>::max() << l_offset;
217227
if (l_index != m_index) {
218-
fn(&storage[l_index++], static_cast<StorageElem>(l_mask));
219-
l_mask = std::numeric_limits<StorageElem>::max();
228+
fn(&storage[l_index++], static_cast<elem_t>(l_mask));
229+
l_mask = std::numeric_limits<elem_t>::max();
220230
}
221231
while (l_index != m_index) {
222-
fn(&storage[l_index++], static_cast<StorageElem>(l_mask));
232+
fn(&storage[l_index++], static_cast<elem_t>(l_mask));
223233
}
224-
auto const m_mask = std::numeric_limits<StorageElem>::max() >>
234+
auto const m_mask = std::numeric_limits<elem_t>::max() >>
225235
(storage_elem_size - m_offset - 1);
226-
fn(&storage[l_index], static_cast<StorageElem>(l_mask & m_mask));
236+
fn(&storage[l_index], static_cast<elem_t>(l_mask & m_mask));
227237
return *this;
228238
}
229239

@@ -245,7 +255,7 @@ template <std::size_t N, typename StorageElem> class bitset {
245255
constexpr auto reset(T idx) LIFETIMEBOUND -> bitset & {
246256
auto const pos = static_cast<std::size_t>(to_underlying(idx));
247257
auto const [index, offset] = indices(pos);
248-
storage[index] &= static_cast<StorageElem>(~(bit << offset));
258+
storage[index] &= static_cast<elem_t>(~(bit << offset));
249259
return *this;
250260
}
251261

@@ -267,7 +277,7 @@ template <std::size_t N, typename StorageElem> class bitset {
267277
template <typename T> constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & {
268278
auto const pos = static_cast<std::size_t>(to_underlying(idx));
269279
auto const [index, offset] = indices(pos);
270-
storage[index] ^= static_cast<StorageElem>(bit << offset);
280+
storage[index] ^= static_cast<elem_t>(bit << offset);
271281
return *this;
272282
}
273283

@@ -310,10 +320,10 @@ template <std::size_t N, typename StorageElem> class bitset {
310320
std::size_t i = 0;
311321
for (auto e : storage) {
312322
if (auto offset = static_cast<std::size_t>(countr_one(e));
313-
offset != std::numeric_limits<StorageElem>::digits) {
323+
offset != std::numeric_limits<elem_t>::digits) {
314324
return i + offset;
315325
}
316-
i += std::numeric_limits<StorageElem>::digits;
326+
i += std::numeric_limits<elem_t>::digits;
317327
}
318328
return i;
319329
}
@@ -358,13 +368,13 @@ template <std::size_t N, typename StorageElem> class bitset {
358368
} else {
359369
auto const borrow_shift = storage_elem_size - pos;
360370
for (auto i = start; i > std::size_t{}; --i) {
361-
storage[dst] = static_cast<StorageElem>(storage[i] << pos);
371+
storage[dst] = static_cast<elem_t>(storage[i] << pos);
362372
storage[dst] |=
363-
static_cast<StorageElem>(storage[i - 1] >> borrow_shift);
373+
static_cast<elem_t>(storage[i - 1] >> borrow_shift);
364374
--dst;
365375
}
366376
}
367-
storage[dst] = static_cast<StorageElem>(storage.front() << pos);
377+
storage[dst] = static_cast<elem_t>(storage.front() << pos);
368378
while (dst > std::size_t{}) {
369379
storage[--dst] = 0;
370380
}
@@ -384,21 +394,21 @@ template <std::size_t N, typename StorageElem> class bitset {
384394
} else {
385395
auto const borrow_shift = storage_elem_size - pos;
386396
for (auto i = start; i < storage_size - 1; ++i) {
387-
storage[dst] = static_cast<StorageElem>(storage[i] >> pos);
397+
storage[dst] = static_cast<elem_t>(storage[i] >> pos);
388398
storage[dst] |=
389-
static_cast<StorageElem>(storage[i + 1] << borrow_shift);
399+
static_cast<elem_t>(storage[i + 1] << borrow_shift);
390400
++dst;
391401
}
392402
}
393-
storage[dst++] = static_cast<StorageElem>(storage.back() >> pos);
403+
storage[dst++] = static_cast<elem_t>(storage.back() >> pos);
394404
while (dst < storage_size) {
395405
storage[dst++] = 0;
396406
}
397407
return *this;
398408
}
399409
};
400410

401-
template <typename F, std::size_t M, typename... S>
411+
template <typename F, auto M, typename... S>
402412
constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
403413
if constexpr (sizeof...(bs) == 1) {
404414
return (bs.for_each(std::forward<F>(f)), ...);
@@ -407,12 +417,9 @@ constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
407417
return f;
408418
}
409419
}
410-
} // namespace detail
411-
412-
template <auto N, typename StorageElem = void>
413-
using bitset =
414-
detail::bitset<to_underlying(N),
415-
decltype(smallest_uint<to_underlying(N), StorageElem>())>;
416420

421+
#if __cplusplus >= 202002L
422+
template <std::size_t N> bitset(ct_string<N>) -> bitset<N - 1>;
423+
#endif
417424
} // namespace v1
418425
} // namespace stdx

0 commit comments

Comments
 (0)