Skip to content

Commit d7c9de1

Browse files
committed
async: add hpx::async<^^func>(target, ...) reflection overload
Adds a new hpx::async overload that accepts a std::meta::info reflection of a free function as a non-type template parameter, eliminating the need to define an explicit action type: // Before: explicit action type required using my_action = hpx::actions::reflect_action<^^app::compute>; hpx::async<my_action>(target, args...); // After: direct reflection syntax hpx::async<^^app::compute>(target, args...); The overload is constrained to accept only viable distributed targets (hpx::id_type, client types, distribution policies), matching the existing async_action_dispatch specializations. Internally it constructs reflect_action<F>{} and delegates to the existing hpx::async(action, target, ...) machinery — no changes to dispatch internals required. Guarded by HPX_HAVE_CXX26_REFLECTION. Suggested by hkaiser as a natural extension of the reflection-based action work from PRs TheHPXProject#7298, TheHPXProject#7311, TheHPXProject#7342, TheHPXProject#7349. Signed-off-by: Priyanshi507 <hiiuiuiabi@gmail.com>
1 parent 6b20020 commit d7c9de1

6 files changed

Lines changed: 110 additions & 0 deletions

File tree

cmake/HPX_CollectStdHeaders.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ set(STANDARD_LIBRARY_HEADERS
105105
"<cwctype>"
106106
)
107107

108+
if(HPX_WITH_CXX26_REFLECTION)
109+
list(APPEND STANDARD_LIBRARY_HEADERS "<meta>")
110+
endif()
111+
108112
# Function to extract #includes from a file recursively
109113
function(hpx_extract_includes_from_file module)
110114

libs/full/actions_base/include/hpx/actions_base/reflect_action.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ namespace hpx::actions {
129129
typename Component =
130130
typename detail::reflect_component_action_base<F>::component_type,
131131
typename Derived = void>
132+
requires(!std::meta::is_function(F))
132133
struct reflect_component_action
133134
: basic_action<Component,
134135
typename detail::reflect_component_action_base<F>::func_type,
@@ -180,6 +181,7 @@ namespace hpx::actions {
180181
typename Component =
181182
typename detail::reflect_component_action_base<F>::component_type,
182183
typename Derived = void>
184+
requires(!std::meta::is_function(F))
183185
struct reflect_component_direct_action
184186
: reflect_component_action<F, Component,
185187
detail::action_type_t<
@@ -232,6 +234,7 @@ namespace hpx::actions {
232234
/// \tparam F A std::meta::info reflection of a free function.
233235
/// \tparam Derived Derived type for CRTP extensibility (default: void).
234236
template <std::meta::info F, typename Derived = void>
237+
requires(std::meta::is_function(F))
235238
struct reflect_action
236239
: basic_action<hpx::actions::detail::plain_function,
237240
typename detail::reflect_action_base<F>::func_type,
@@ -292,6 +295,7 @@ namespace hpx::actions {
292295
/// \tparam F A std::meta::info reflection of a free function.
293296
/// \tparam Derived CRTP derived type (defaults to void).
294297
template <std::meta::info F, typename Derived = void>
298+
requires(std::meta::is_function(F))
295299
struct reflect_direct_action
296300
: reflect_action<F,
297301
detail::action_type_t<reflect_direct_action<F, Derived>, Derived>>

libs/full/actions_base/tests/unit/reflect_action_test.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ int main()
141141
hpx::naming::component_type comptype{};
142142
direct_broadcast::invoke(lva, comptype, 42);
143143
}
144+
// Test: hpx::async<^^func>(target, ...) overload is directly callable
145+
// (compile-time check -- full runtime dispatch requires a live HPX locality)
146+
{
147+
// Verify the overload is well-formed for id_type targets
148+
static_assert(
149+
requires(hpx::id_type id) {
150+
hpx::async<^^app::compute>(id, 3.0, 4.0);
151+
}, "hpx::async<^^func>(id_type, ...) must be well-formed");
152+
// Verify non-function reflections are rejected by the constraint
153+
static_assert(
154+
!requires(hpx::id_type id) { hpx::async<^^int>(id, 3.0, 4.0); },
155+
"hpx::async<^^non-function> must be ill-formed");
156+
}
144157
return hpx::util::report_errors();
145158
}
146159

libs/full/async_distributed/include/hpx/async_distributed/async.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,28 @@ namespace hpx {
218218
return detail::async_action_dispatch<Action, std::decay_t<F>>::call(
219219
HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...);
220220
}
221+
222+
#if defined(HPX_HAVE_CXX26_REFLECTION)
223+
/// \brief Reflection-based async overload.
224+
///
225+
/// Allows calling hpx::async<^^func>(target, ...) directly without
226+
/// defining an explicit action type. Internally constructs
227+
/// reflect_action<F> and delegates to the existing async machinery.
228+
///
229+
/// \tparam F A std::meta::info reflection of a free function.
230+
/// \tparam Target id_type, client, or distribution policy.
231+
/// \tparam Ts Additional arguments to pass to the function.
232+
template <std::meta::info F, typename Target, typename... Ts>
233+
requires(std::meta::is_function(F) &&
234+
(std::is_same_v<std::decay_t<Target>, hpx::id_type> ||
235+
hpx::traits::is_client_v<std::decay_t<Target>> ||
236+
hpx::traits::is_distribution_policy_v<std::decay_t<Target>>) )
237+
HPX_CXX_EXPORT HPX_FORCEINLINE auto async(Target&& target, Ts&&... ts)
238+
{
239+
return hpx::async(hpx::actions::reflect_action<F>{},
240+
HPX_FORWARD(Target, target), HPX_FORWARD(Ts, ts)...);
241+
}
242+
#endif // HPX_HAVE_CXX26_REFLECTION
221243
} // namespace hpx
222244

223245
///////////////////////////////////////////////////////////////////////////////

libs/full/async_distributed/tests/unit/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ set(tests
3434
set(async_continue_PARAMETERS LOCALITIES 2)
3535
set(async_continue_cb_PARAMETERS LOCALITIES 2)
3636
set(async_remote_PARAMETERS LOCALITIES 2)
37+
38+
if(HPX_WITH_CXX26_REFLECTION)
39+
set(tests ${tests} async_remote_reflect)
40+
set(async_remote_reflect_PARAMETERS LOCALITIES 2)
41+
endif()
3742
set(async_remote_client_PARAMETERS LOCALITIES 2)
3843
set(async_cb_remote_PARAMETERS LOCALITIES 2)
3944
set(async_cb_remote_client_PARAMETERS LOCALITIES 2)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2026 Priyanshi Sharma
2+
//
3+
// SPDX-License-Identifier: BSL-1.0
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
7+
#include <hpx/config.hpp>
8+
#if !defined(HPX_COMPUTE_DEVICE_CODE)
9+
#if defined(HPX_HAVE_CXX26_REFLECTION)
10+
11+
#include <hpx/hpx_init.hpp>
12+
#include <hpx/include/actions.hpp>
13+
#include <hpx/include/async.hpp>
14+
#include <hpx/include/runtime.hpp>
15+
#include <hpx/modules/testing.hpp>
16+
17+
#include <cstdint>
18+
#include <vector>
19+
20+
///////////////////////////////////////////////////////////////////////////////
21+
std::int32_t reflect_increment(std::int32_t i)
22+
{
23+
return i + 1;
24+
}
25+
HPX_PLAIN_ACTION(reflect_increment)
26+
27+
///////////////////////////////////////////////////////////////////////////////
28+
void test_async_reflect(hpx::id_type const& target)
29+
{
30+
// Test: hpx::async<^^func>(target, ...) -- reflection-based syntax
31+
// No explicit action type needed
32+
{
33+
hpx::future<std::int32_t> f1 =
34+
hpx::async<^^reflect_increment>(target, 42);
35+
HPX_TEST_EQ(f1.get(), 43);
36+
}
37+
{
38+
hpx::future<std::int32_t> f1 =
39+
hpx::async<^^reflect_increment>(hpx::launch::async, target, 42);
40+
HPX_TEST_EQ(f1.get(), 43);
41+
}
42+
}
43+
44+
int hpx_main()
45+
{
46+
std::vector<hpx::id_type> localities = hpx::find_all_localities();
47+
for (hpx::id_type const& id : localities)
48+
{
49+
test_async_reflect(id);
50+
}
51+
return hpx::finalize();
52+
}
53+
54+
int main(int argc, char* argv[])
55+
{
56+
HPX_TEST_EQ_MSG(
57+
hpx::init(argc, argv), 0, "HPX main exited with non-zero status");
58+
return hpx::util::report_errors();
59+
}
60+
61+
#endif // HPX_HAVE_CXX26_REFLECTION
62+
#endif // HPX_COMPUTE_DEVICE_CODE

0 commit comments

Comments
 (0)