Skip to content

Commit 1b1731a

Browse files
Add Bulk Algorithm (#129)
* Added Doxygen-Awesome style sheets to give a more modern look to Doxygen. * Set up bulk algorithm * Implement completion signatures and add bulk test with additional tests * run clang format * fix typo * Align Bulk Adaptor Sender completion signatures with base sender * Deduce function signature for sender to ensure proper completion * Added test for noexcept lambda and updated fixed signal to not deduce function signature * Resolved merge conflicts by keeping changes from main * Refactor complete lambda to use product_type and std::is_invocable_v * Workaround for missing auto(x) in MSVC by using std::remove_cvref_t * remove auto(x)
1 parent 72262a2 commit 1b1731a

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// include/beman/execution/detail/bulk.hpp -*-C++-*-
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_BULK
5+
#define INCLUDED_BEMAN_EXECUTION_DETAIL_BULK
6+
7+
#include <beman/execution/detail/get_completion_signatures.hpp>
8+
#include <beman/execution/detail/meta_combine.hpp>
9+
#include <beman/execution/detail/meta_unique.hpp>
10+
#include <beman/execution/detail/basic_sender.hpp>
11+
#include <beman/execution/detail/completion_signatures.hpp>
12+
#include <beman/execution/detail/completion_signatures_for.hpp>
13+
#include <beman/execution/detail/get_domain_early.hpp>
14+
#include <beman/execution/detail/make_sender.hpp>
15+
#include <beman/execution/detail/movable_value.hpp>
16+
#include <beman/execution/detail/product_type.hpp>
17+
#include <beman/execution/detail/sender.hpp>
18+
#include <beman/execution/detail/set_error.hpp>
19+
#include <beman/execution/detail/transform_sender.hpp>
20+
#include <beman/execution/detail/default_impls.hpp>
21+
#include <beman/execution/detail/impls_for.hpp>
22+
#include <beman/execution/detail/set_value.hpp>
23+
#include <algorithm>
24+
#include <concepts>
25+
#include <exception>
26+
#include <type_traits>
27+
#include <utility>
28+
29+
#include <beman/execution/detail/suppress_push.hpp>
30+
namespace beman::execution::detail {
31+
32+
struct bulk_t {
33+
34+
template <class Sender, class Shape, class f>
35+
requires(::beman::execution::sender<Sender> && std::is_integral_v<Shape> &&
36+
::beman::execution::detail::movable_value<f>)
37+
auto operator()(Sender&& sndr, Shape&& shape, f&& fun) const {
38+
39+
auto domain{::beman::execution::detail::get_domain_early(sndr)};
40+
41+
return ::beman::execution::transform_sender(
42+
domain,
43+
::beman::execution::detail::make_sender(
44+
*this, ::beman::execution::detail::product_type<Shape, f>{shape, fun}, std::forward<Sender>(sndr)));
45+
}
46+
};
47+
48+
template <>
49+
struct impls_for<bulk_t> : ::beman::execution::detail::default_impls {
50+
51+
static constexpr auto complete = []<class Index, class Shape, class Fun, class Rcvr, class Tag, class... Args>(
52+
Index,
53+
::beman::execution::detail::product_type<Shape, Fun>& state,
54+
Rcvr& rcvr,
55+
Tag,
56+
Args&&... args) noexcept -> void
57+
requires(not::std::same_as<Tag, set_value_t> || std::is_invocable_v<Fun, Shape, Args...>)
58+
{
59+
if constexpr (std::same_as<Tag, set_value_t>) {
60+
auto& [shape, f] = state;
61+
62+
using s_type = std::remove_cvref_t<decltype(shape)>;
63+
64+
constexpr bool nothrow = noexcept(f(s_type(shape), args...));
65+
66+
try {
67+
[&]() noexcept(nothrow) {
68+
for (decltype(s_type(shape)) i = 0; i < shape; i++) {
69+
f(s_type(i), args...);
70+
}
71+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
72+
}();
73+
74+
} catch (...) {
75+
if constexpr (not nothrow) {
76+
::beman::execution::set_error(std::move(rcvr), std::current_exception());
77+
}
78+
}
79+
} else {
80+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
81+
}
82+
};
83+
};
84+
85+
template <typename, typename, typename>
86+
struct fixed_completions_helper;
87+
88+
template <typename F, typename Shape, typename... Args>
89+
struct fixed_completions_helper<F, Shape, completion_signatures<Args...>> {
90+
91+
template <typename, typename>
92+
struct may_throw;
93+
template <typename XF, typename Tag, typename... XArgs>
94+
struct may_throw<XF, Tag(XArgs...)> {
95+
static constexpr bool value = std::same_as<Tag, ::beman::execution::set_value_t> &&
96+
not::std::is_nothrow_invocable<XF, Shape, XArgs...>();
97+
};
98+
template <typename XF, typename... Sigs>
99+
struct may_throw<XF, completion_signatures<Sigs...>> {
100+
static constexpr bool value = (false || ... || may_throw<XF, Sigs>::value);
101+
};
102+
103+
using type = std::conditional_t<!may_throw<F, Args...>::value,
104+
completion_signatures<Args...>,
105+
completion_signatures<Args..., set_error_t(std::exception_ptr)>>;
106+
};
107+
108+
template <typename F, typename Shape, typename Completions>
109+
using fixed_completions = typename fixed_completions_helper<F, Shape, Completions>::type;
110+
111+
template <class Shape, class F, class Sender, class Env>
112+
struct completion_signatures_for_impl<
113+
::beman::execution::detail::
114+
basic_sender<::beman::execution::detail::bulk_t, ::beman::execution::detail::product_type<Shape, F>, Sender>,
115+
Env> {
116+
117+
using completions = decltype(get_completion_signatures(std::declval<Sender>(), std::declval<Env>()));
118+
using type = ::beman::execution::detail::meta::unique<
119+
::beman::execution::detail::meta::combine<fixed_completions<F, Shape, completions>>>;
120+
};
121+
122+
} // namespace beman::execution::detail
123+
124+
#include <beman/execution/detail/suppress_pop.hpp>
125+
126+
namespace beman::execution {
127+
128+
using ::beman::execution::detail::bulk_t;
129+
inline constexpr ::beman::execution::bulk_t bulk{};
130+
131+
} // namespace beman::execution
132+
133+
#endif

tests/beman/execution/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ list(
5757
exec-when-all.test
5858
exec-with-awaitable-senders.test
5959
execution-queryable-concept.test
60+
exec-bulk.test
6061
execution-syn.test
6162
forward-like.test
6263
function-objects.test
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// src/beman/execution/tests/exec-bulk.test.cpp -*-C++-*-
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include "beman/execution/detail/get_completion_signatures.hpp"
5+
#include "beman/execution/detail/get_env.hpp"
6+
#include "beman/execution/detail/sync_wait.hpp"
7+
#include <cstdlib>
8+
#include <test/execution.hpp>
9+
#include <beman/execution/detail/bulk.hpp>
10+
#include <beman/execution/detail/just.hpp>
11+
#include <vector>
12+
13+
namespace {
14+
auto test_bulk() {
15+
auto b0 = test_std::bulk(test_std::just(), 1, [](int) {});
16+
17+
static_assert(test_std::sender<decltype(b0)>);
18+
auto b0_env = test_std::get_env(b0);
19+
auto b0_completions = test_std::get_completion_signatures(b0, b0_env);
20+
static_assert(
21+
std::is_same_v<decltype(b0_completions),
22+
beman::execution::completion_signatures<beman::execution::set_value_t(),
23+
beman::execution::set_error_t(std::exception_ptr)> >,
24+
"Completion signatures do not match!");
25+
26+
int counter = 0;
27+
28+
auto b1 = test_std::bulk(test_std::just(), 5, [&](int i) { counter += i; });
29+
30+
static_assert(test_std::sender<decltype(b1)>);
31+
auto b1_env = test_std::get_env(b0);
32+
auto b1_completions = test_std::get_completion_signatures(b1, b1_env);
33+
static_assert(
34+
std::is_same_v<decltype(b1_completions),
35+
beman::execution::completion_signatures<beman::execution::set_value_t(),
36+
beman::execution::set_error_t(std::exception_ptr)> >,
37+
"Completion signatures do not match!");
38+
test_std::sync_wait(b1);
39+
ASSERT(counter == 10);
40+
41+
std::vector<int> a{1, 2, 3, 4, 5, 6, 7, 8};
42+
std::vector<int> b{9, 10, 11, 13, 14, 15, 16, 17};
43+
44+
std::vector<int> results(a.size(), 0);
45+
46+
auto b2 = test_std::bulk(test_std::just(a), a.size(), [&](std::size_t index, const std::vector<int>& vec) {
47+
results[index] = vec[index] * b[index];
48+
});
49+
static_assert(test_std::sender<decltype(b2)>);
50+
auto b2_env = test_std::get_env(b2);
51+
auto b2_completions = test_std::get_completion_signatures(b2, b2_env);
52+
static_assert(
53+
std::is_same_v<decltype(b2_completions),
54+
beman::execution::completion_signatures<beman::execution::set_value_t(std::vector<int>),
55+
beman::execution::set_error_t(std::exception_ptr)> >,
56+
"Completion signatures do not match!");
57+
test_std::sync_wait(b2);
58+
59+
// Expected results: element-wise multiplication of a and b
60+
std::vector<int> expected{9, 20, 33, 52, 70, 90, 112, 136};
61+
62+
for (size_t i = 0; i < results.size(); ++i) {
63+
ASSERT(results[i] == expected[i]);
64+
}
65+
}
66+
67+
auto test_bulk_noexept() {
68+
auto b0 = test_std::bulk(test_std::just(), 1, [](int) noexcept {});
69+
auto b0_env = test_std::get_env(b0);
70+
auto b0_completions = test_std::get_completion_signatures(b0, b0_env);
71+
static_assert(std::is_same_v<decltype(b0_completions),
72+
beman::execution::completion_signatures<beman::execution::set_value_t()> >,
73+
"Completion signatures do not match!");
74+
static_assert(test_std::sender<decltype(b0)>);
75+
76+
int counter = 0;
77+
78+
auto b1 = test_std::bulk(test_std::just(), 5, [&](int i) noexcept { counter += i; });
79+
80+
static_assert(test_std::sender<decltype(b1)>);
81+
auto b1_env = test_std::get_env(b0);
82+
auto b1_completions = test_std::get_completion_signatures(b1, b1_env);
83+
static_assert(std::is_same_v<decltype(b1_completions),
84+
beman::execution::completion_signatures<beman::execution::set_value_t()> >,
85+
"Completion signatures do not match!");
86+
test_std::sync_wait(b1);
87+
ASSERT(counter == 10);
88+
}
89+
90+
} // namespace
91+
92+
TEST(exec_bulk) {
93+
94+
try {
95+
96+
test_bulk();
97+
test_bulk_noexept();
98+
99+
} catch (...) {
100+
101+
ASSERT(nullptr == "the bulk tests shouldn't throw");
102+
}
103+
104+
return EXIT_SUCCESS;
105+
}

0 commit comments

Comments
 (0)