diff --git a/libs/full/actions_base/include/hpx/actions_base/reflect_action.hpp b/libs/full/actions_base/include/hpx/actions_base/reflect_action.hpp index 15eb4f2443c..1ac41ed11fb 100644 --- a/libs/full/actions_base/include/hpx/actions_base/reflect_action.hpp +++ b/libs/full/actions_base/include/hpx/actions_base/reflect_action.hpp @@ -6,43 +6,52 @@ /// \file reflect_action.hpp /// \brief Reflection-based action definition for HPX remote operations. -/// -/// This header provides reflect_action, a C++26 reflection-based -/// replacement for the HPX_PLAIN_ACTION and HPX_REGISTER_ACTION macros. -/// Instead of verbose boilerplate, users write a single line: -/// -/// using compute_action = HPX_ACTION(app::compute); -/// -/// The action name, function pointer type, arity, and registration are -/// all derived automatically at compile time using C++26 static reflection. - #pragma once #include #if defined(HPX_HAVE_CXX26_REFLECTION) -#include +#include +#include +#include #include #include +#include #include namespace hpx::actions { + /// \cond NOINTERNAL + namespace detail { + + /// Helper to extract function type from reflection for use + /// as basic_action template argument (two-step workaround for + /// GCC/Clang restriction on splice expressions as template args). + template + struct reflect_action_base + { + using func_type = [:std::meta::type_of(F):]; + using type = basic_action>; + }; + + } // namespace detail + /// \endcond + /// \brief Reflection-based action template. /// - /// reflect_action provides the same interface as HPX_PLAIN_ACTION - /// but derives all properties automatically from the reflected function F: - /// - Qualified name via scope_builder - /// - Function pointer type via std::meta::type_of(F) - /// - Function pointer via splicing [:F:] - /// - Arity via std::meta::parameters_of(F).size() + /// reflect_action integrates with HPX's action system by inheriting + /// from basic_action>. + /// All properties are derived automatically from the reflected function F. /// /// \tparam F A std::meta::info reflection of a free function. - /// Obtain via the reflection operator: ^^app::my_function template struct reflect_action + : basic_action::func_type, + reflect_action> { /// The function type (e.g. int(double, double)) using func_type = [:std::meta::type_of(F):]; @@ -57,16 +66,23 @@ namespace hpx::actions { static constexpr auto name_storage = hpx::serialization::detail::scope_builder::value; - /// Number of parameters the function takes - static constexpr std::size_t arity = std::meta::parameters_of(F).size(); + /// Returns the action name in HPX format: "plain action(app::compute)" + static std::string get_action_name( + naming::address::address_type /*lva*/) + { + return hpx::actions::detail::make_plain_action_name( + std::string_view(name_storage.data, name_storage.size)); + } - /// Returns the fully qualified name of the action. - /// Called by hpx::actions::detail::register_action during - /// static initialization to register this action with the - /// HPX action registry. - static consteval char const* get_action_name() noexcept + /// Invokes the reflected function with the given arguments. + template + static auto invoke(naming::address::address_type /*lva*/, + naming::address::component_type /*comptype*/, Ts&&... vs) { - return name_storage.data; + using base_t = basic_action>; + base_t::increment_invocation_count(); + return func_ptr(HPX_FORWARD(Ts, vs)...); } }; diff --git a/libs/full/actions_base/tests/unit/reflect_action_test.cpp b/libs/full/actions_base/tests/unit/reflect_action_test.cpp index 76168c2b36c..58a2676f75f 100644 --- a/libs/full/actions_base/tests/unit/reflect_action_test.cpp +++ b/libs/full/actions_base/tests/unit/reflect_action_test.cpp @@ -47,29 +47,29 @@ int main() // Test: action name extraction for simple namespace function { HPX_ACTION(app::compute, compute_action); - HPX_TEST_EQ(std::string(compute_action::get_action_name()), - std::string("app::compute")); + HPX_TEST_EQ(std::string(compute_action::get_action_name(nullptr)), + std::string("plain action(app::compute)")); } // Test: action name extraction for void noexcept function { HPX_ACTION(app::broadcast, broadcast_action); - HPX_TEST_EQ(std::string(broadcast_action::get_action_name()), - std::string("app::broadcast")); + HPX_TEST_EQ(std::string(broadcast_action::get_action_name(nullptr)), + std::string("plain action(app::broadcast)")); } // Test: action name extraction for multiple parameters { HPX_ACTION(app::transform, transform_action); - HPX_TEST_EQ(std::string(transform_action::get_action_name()), - std::string("app::transform")); + HPX_TEST_EQ(std::string(transform_action::get_action_name(nullptr)), + std::string("plain action(app::transform)")); } // Test: action name extraction for nested namespace { HPX_ACTION(app::nested::deep_compute, deep_action); - HPX_TEST_EQ(std::string(deep_action::get_action_name()), - std::string("app::nested::deep_compute")); + HPX_TEST_EQ(std::string(deep_action::get_action_name(nullptr)), + std::string("plain action(app::nested::deep_compute)")); } // Test: arity extraction