Skip to content

Commit 61c1980

Browse files
committed
✨ Add transform_reduce on bitset
Problem: - It is relatively common to want to do for_each on a bitset, but also capture whether or not the action succeeded at all, e.g. when handling a message with an indexed handler. Solution: - Add `transform_reduce` on bitset. Note: - Both `for_each` and `transform_reduce` are applicable variadically, however this is not yet implemented. But this is the reason that the bitset is the final argument. - There are some differences (for the better) from the `std::transform_reduce` API. - The transform function comes before the reduction function. `std::transform_reduce` is confusing, because the transform happens first but is given second. - The type of the `init` value can be easily given in a template argument; this is helpful for avoiding warnings when passing `0` (an `int`) to accumulate potentially larger integral types.
1 parent 62b9868 commit 61c1980

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

include/stdx/bitset.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ class bitset {
117117
template <typename F, auto M, typename... S>
118118
friend constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F;
119119

120+
template <typename T, typename F, typename R>
121+
constexpr auto transform_reduce(F &&f, R &&r, T init) const -> T {
122+
std::size_t i = 0;
123+
for (auto e : storage) {
124+
while (e != 0) {
125+
auto const offset = static_cast<std::size_t>(countr_zero(e));
126+
e &= static_cast<elem_t>(~(bit << offset));
127+
init = r(std::move(init), f(i + offset));
128+
}
129+
i += std::numeric_limits<elem_t>::digits;
130+
}
131+
return init;
132+
}
133+
134+
template <typename T, typename F, typename R, auto M, typename... S>
135+
friend constexpr auto transform_reduce(F &&f, R &&r, T init,
136+
bitset<M, S> const &...bs) -> T;
137+
120138
public:
121139
constexpr bitset() = default;
122140
constexpr explicit bitset(std::uint64_t value) {
@@ -421,6 +439,19 @@ constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
421439
}
422440
}
423441

442+
template <typename T, typename F, typename R, auto M, typename... S>
443+
constexpr auto transform_reduce(F &&f, R &&r, T init,
444+
bitset<M, S> const &...bs) -> T {
445+
if constexpr (sizeof...(bs) == 1) {
446+
return (bs.transform_reduce(std::forward<F>(f), std::forward<R>(r),
447+
std::move(init)),
448+
...);
449+
} else {
450+
static_assert(stdx::always_false_v<F>, "unimplemented");
451+
return init;
452+
}
453+
}
454+
424455
#if __cplusplus >= 202002L
425456
template <std::size_t N> bitset(ct_string<N>) -> bitset<N - 1>;
426457
#endif

test/bitset.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,20 @@ TEMPLATE_TEST_CASE("for_each iterates in order lsb to msb", "[bitset]",
331331
CHECK(result == bs);
332332
}
333333

334+
TEMPLATE_TEST_CASE("transform_reduce", "[bitset]", std::uint8_t, std::uint16_t,
335+
std::uint32_t, std::uint64_t) {
336+
constexpr auto bs = stdx::bitset<8, TestType>{0b10101010ul};
337+
int calls{};
338+
auto const result = transform_reduce(
339+
[&](auto i) {
340+
++calls;
341+
return i == 3 and calls == 2;
342+
},
343+
std::logical_or{}, false, bs);
344+
CHECK(result);
345+
CHECK(calls == bs.count());
346+
}
347+
334348
TEMPLATE_TEST_CASE("set range of bits (lsb, length)", "[bitset]", std::uint8_t,
335349
std::uint16_t, std::uint32_t, std::uint64_t) {
336350
using namespace stdx::literals;

0 commit comments

Comments
 (0)