Skip to content

Commit

Permalink
Split static_for into static_for and static_for_while
Browse files Browse the repository at this point in the history
This also improves the implementations a bit to reduce the template
instantiation spam.
  • Loading branch information
jimporter committed May 6, 2024
1 parent 6cbc5fe commit 05e12b3
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 43 deletions.
57 changes: 35 additions & 22 deletions include/mettle/detail/tuple_algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,46 @@

namespace mettle::detail {

template<std::size_t I, std::size_t N>
struct do_iterate {
template<typename Func>
do_iterate(Func &&f) {
constexpr std::integral_constant<std::size_t, I> index{};
if constexpr(std::is_same_v<decltype(f(index)), void>) {
f(index);
do_iterate<I+1, N>(std::forward<Func>(f));
} else {
if(!f(index))
do_iterate<I+1, N>(std::forward<Func>(f));
}
template<typename Func, std::size_t ...I>
constexpr void static_for_impl(Func &&f, std::index_sequence<I...>) {
(f(std::integral_constant<std::size_t, I>{}),...);
}

template<std::size_t N, typename Func>
constexpr void static_for(Func &&f) {
static_for_impl(std::forward<Func>(f), std::make_index_sequence<N>{});
}

template<std::size_t I, typename Func>
constexpr bool static_for_while_impl(Func &&f) {
std::integral_constant<std::size_t, I> index{};
if constexpr(I == 0) {
return f(index);
} else {
if(static_for_while_impl<I-1>(f))
return f(index);
return false;
}
};
}

template<std::size_t N>
struct do_iterate<N, N> {
do_iterate(...) {}
};
template<std::size_t N, typename Func>
constexpr void static_for_while(Func &&f) {
if constexpr(N > 0)
static_for_while_impl<N-1>(std::forward<Func>(f));
}

template<std::size_t N>
using static_for = do_iterate<0, N>;
template<typename Tuple, typename Func>
constexpr void tuple_for_each(Tuple &&tuple, Func &&f) {
constexpr auto N = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_for<N>([&](auto i) {
return f(std::get<decltype(i)::value>(tuple));
});
}

template<typename Tuple, typename Func>
void tuple_for_each(Tuple &&tuple, Func &&f) {
using T = typename std::remove_reference<Tuple>::type;
static_for<std::tuple_size<T>::value>([&](auto i) {
constexpr void tuple_for_each_while(Tuple &&tuple, Func &&f) {
constexpr auto N = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_for_while<N>([&](auto i) {
return f(std::get<decltype(i)::value>(tuple));
});
}
Expand Down
23 changes: 12 additions & 11 deletions include/mettle/matchers/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,20 @@ namespace mettle {
auto i = begin(actual), end_i = end(actual);

ss << "[";
tuple_for_each(matchers_, [&i, &end_i, &good, &ola](const auto &m) {
if(i == end_i) {
good = false;
tuple_for_each_while(
matchers_, [&i, &end_i, &good, &ola](const auto &m) {
if(i == end_i) {
good = false;
return false;
}

auto result = m(*i);
good &= result;
ola(matcher_message(result, *i));
++i;
return true;
}

auto result = m(*i);
good &= result;
ola(matcher_message(result, *i));
++i;
return false;
});
);

// Print any remaining expected values (if `actual` is longer than the
// list of matchers).
Expand Down Expand Up @@ -227,7 +229,6 @@ namespace mettle {
auto result = get<i>(matchers_)(v);
good &= result;
ola(matcher_message(result, v));
return false;
});
ss << "]";

Expand Down
4 changes: 2 additions & 2 deletions include/mettle/matchers/combinatoric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ namespace mettle {
template<typename U>
match_result operator ()(U &&actual) const {
match_result result = initial_;
tuple_for_each(matchers_, [&, this](const auto &matcher) {
tuple_for_each_while(matchers_, [&, this](const auto &matcher) {
auto m = matcher(actual);
bool done = reducer_(initial_, m) != initial_;
if(done)
result = std::move(m);
return done;
return !done;
});
return result;
}
Expand Down
64 changes: 56 additions & 8 deletions test/test_tuple_algorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,72 @@ suite<> test_tuple_alg("tuple algorithms", [](auto &_) {
expect("number of iterations", count, equal_to(2));
});

_.test("early exit", []() {
std::tuple<int, std::string> tup(1, "two");
_.test("modification", []() {
std::tuple<int, int> tup(1, 2);
tuple_for_each(tup, [](auto &&item) {
item++;
});

expect(tup, equal_to(std::make_tuple(2, 3)));
});
});

subsuite<>(_, "tuple_for_each_while()", [](auto &_) {
using detail::tuple_for_each_while;

_.test("empty tuple", []() {
std::tuple<> tup;
std::size_t count = 0;
tuple_for_each(tup, [&count](auto &&) {
tuple_for_each_while(tup, [&count](auto &&) {
count++;
return true;
});

expect("number of iterations", count, equal_to(0));
});

_.test("1-tuple", []() {
std::tuple<int> tup(1);
std::size_t count = 0;
tuple_for_each_while(tup, [&count](auto &&item) {
expect("tuple value", item, equal_to(1));
count++;
return true;
});

expect("number of iterations", count, equal_to(1));
});

_.test("modification", []() {
std::tuple<int, int> tup(1, 2);
tuple_for_each(tup, [](auto &&item) {
item++;
_.test("2-tuple", []() {
std::tuple<int, std::string> tup(1, "two");
std::size_t count = 0;
tuple_for_each_while(tup, [&count](auto &&) {
count++;
return true;
});

expect(tup, equal_to(std::make_tuple(2, 3)));
expect("number of iterations", count, equal_to(2));

std::tuple<int, int> tup2(0, 1);
count = 0;
tuple_for_each_while(tup2, [&count](auto &&item) {
expect("tuple value", item, equal_to(count));
count++;
return true;
});

expect("number of iterations", count, equal_to(2));
});

_.test("early exit", []() {
std::tuple<int, std::string> tup(1, "two");
std::size_t count = 0;
tuple_for_each_while(tup, [&count](auto &&) {
count++;
return false;
});

expect("number of iterations", count, equal_to(1));
});
});

Expand Down

0 comments on commit 05e12b3

Please sign in to comment.