Skip to content

Commit d522680

Browse files
snarkmasterfacebook-github-bot
authored andcommitted
Add reverse_apply
Summary: Using this for `async_closure` cleanup -- to mirror normal scope rules, the captures tuple needs to be stored in the opposite order from the order of `co_cleanup` invocations. Reviewed By: ispeters Differential Revision: D71060463 fbshipit-source-id: 0fce48dc4e859f5bf89369824d2eaca5b1ca4ef7
1 parent 293db02 commit d522680

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

folly/detail/test/tuple_test.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct TupleTests {
139139
test(17 == TupFn{}.apply([]() { return 17; }, empty));
140140

141141
Tup t{1, 2, 3};
142+
TupFn{}.apply([t](auto... n) { test(t == Tup{n...}); }, t);
142143
test(6 == TupFn{}.apply([](auto... n) { return (n + ...); }, t));
143144
TupFn{}.apply([](auto&... n) { return ((n *= n), ...); }, t);
144145
test(Tup{1, 4, 9} == t);
@@ -147,11 +148,11 @@ struct TupleTests {
147148
}
148149

149150
static constexpr bool check_apply_move() {
150-
Tup moveT{MoveOnly{}, MoveOnly{}};
151+
Tup moveT{MoveOnly{}, std::optional<MoveOnly>{}};
151152
auto m = TupFn{}.apply(
152153
[](auto&& m1, auto m2) {
153154
static_assert(std::is_same_v<decltype(m1), MoveOnly&&>);
154-
static_assert(std::is_same_v<decltype(m2), MoveOnly>);
155+
static_assert(std::is_same_v<decltype(m2), std::optional<MoveOnly>>);
155156
return std::move(m1);
156157
},
157158
std::move(moveT));
@@ -335,6 +336,56 @@ TEST(LiteTupleTest, lite_tuple_cat_move_unique_ptr) {
335336
LiteTests::check_tuple_cat_move_unique_ptr();
336337
}
337338

339+
constexpr bool check_lite_tuple_reverse_apply() { // no `std::reverse_apply`
340+
lite_tuple::tuple empty{};
341+
test(17 == lite_tuple::reverse_apply([]() { return 17; }, empty));
342+
343+
lite_tuple::tuple t{1, 2, 3};
344+
lite_tuple::reverse_apply(
345+
[](auto... n) {
346+
test(lite_tuple::tuple{3, 2, 1} == lite_tuple::tuple{n...});
347+
},
348+
t);
349+
lite_tuple::reverse_apply([](auto&... n) { return ((n *= n), ...); }, t);
350+
test(lite_tuple::tuple{1, 4, 9} == t);
351+
352+
lite_tuple::tuple moveT{MoveOnly{}, std::optional<MoveOnly>{}};
353+
auto m = lite_tuple::reverse_apply(
354+
[](auto&& m1, auto m2) {
355+
static_assert(std::is_same_v<decltype(m1), std::optional<MoveOnly>&&>);
356+
static_assert(std::is_same_v<decltype(m2), MoveOnly>);
357+
return std::move(m1);
358+
},
359+
std::move(moveT));
360+
static_assert(std::is_same_v<decltype(m), std::optional<MoveOnly>>);
361+
362+
int n = 5;
363+
lite_tuple::reverse_apply(
364+
[](auto&& lref, auto&& rref) {
365+
static_assert(std::is_same_v<int&&, decltype(rref)>);
366+
static_assert(std::is_same_v<int&, decltype(lref)>);
367+
},
368+
// NOLINTNEXTLINE(performance-move-const-arg)
369+
// @lint-ignore CLANGTIDY facebook-hte-MoveEvaluationOrder
370+
lite_tuple::forward_as_tuple(std::move(n), n));
371+
372+
return true;
373+
}
374+
375+
static_assert(check_lite_tuple_reverse_apply());
376+
377+
TEST(LiteTupleTest, lite_tuple_reverse_apply) { // no `std::reverse_apply`
378+
lite_tuple::tuple moveT{std::make_unique<int>(7), MoveOnly{}};
379+
auto p = lite_tuple::reverse_apply(
380+
[](auto m, auto&& p) {
381+
static_assert(std::is_same_v<decltype(m), MoveOnly>);
382+
return std::move(p);
383+
},
384+
std::move(moveT));
385+
EXPECT_EQ(7, *p);
386+
EXPECT_EQ(nullptr, lite_tuple::get<0>(moveT).get());
387+
}
388+
338389
} // namespace folly::detail
339390

340391
FOLLY_POP_WARNING

folly/detail/tuple.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,16 @@ FOLLY_ALWAYS_INLINE constexpr decltype(auto) apply(auto&& fn, auto&& tup) {
172172
}(std::make_index_sequence<std::tuple_size_v<tupv>>{});
173173
}
174174

175+
FOLLY_ALWAYS_INLINE constexpr decltype(auto) reverse_apply(
176+
auto&& fn, auto&& tup) {
177+
using tupv = std::remove_reference_t<decltype(tup)>;
178+
constexpr size_t Last = std::tuple_size_v<tupv> - 1;
179+
return [&]<size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
180+
return static_cast<decltype(fn)>(fn)(
181+
get<Last - Is>(static_cast<decltype(tup)>(tup))...);
182+
}(std::make_index_sequence<Last + 1>{});
183+
}
184+
175185
// `tuple_cat` implementation details. Credit: This follows the `tuplet`
176186
// algorithm, which in turn appears to derive from Eric Niebler's
177187
// `tuple_cat.cpp` -- read its docs for another explanation:
@@ -193,8 +203,8 @@ consteval auto repeat_type(tag_t<ForEach...>) {
193203
// `Base` is a `struct entry` base class of a tuple-of-tuples. Returns the
194204
// `tuple_base_list` of the inner tuple "indexed" by this `Base`.
195205
template <typename Base>
196-
using inner_tuple_base_list_t =
197-
std::remove_reference_t<typename Base::entry_type>::tuple_base_list;
206+
using inner_tuple_base_list_t = typename std::remove_reference_t<
207+
typename Base::entry_type>::tuple_base_list;
198208

199209
// Given the bases of some `outerTuples...`, return a `tag_t` repeating the
200210
// corresponding base for each of the tuple's entries (cardinality of

0 commit comments

Comments
 (0)