Skip to content

Commit e2c22d4

Browse files
committed
Add const reference service callback signatures
AnyServiceCallback now also accepts void (const Request &, Response &) void (const rmw_request_id_t &, const Request &, Response &) in addition to the existing shared_ptr signatures. The request and response are still received as shared_ptr internally and are dereferenced before calling the user callback, mirroring what AnySubscriptionCallback does for const MessageT & callbacks. Both set() overloads gain matching function_traits::same_arguments branches so std::bind results resolve to the right alternative, and dispatch() handles the two new alternatives. Existing overloads are unchanged. Signed-off-by: KR Ravindra <42912207+KR-Ravindra@users.noreply.github.com>
1 parent e47c084 commit e2c22d4

3 files changed

Lines changed: 162 additions & 1 deletion

File tree

rclcpp/include/rclcpp/any_service_callback.hpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ class AnyServiceCallback
9696
>::value)
9797
{
9898
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
99+
} else if constexpr ( // NOLINT
100+
rclcpp::function_traits::same_arguments<
101+
CallbackT,
102+
ConstRefCallback
103+
>::value)
104+
{
105+
callback_.template emplace<ConstRefCallback>(callback);
106+
} else if constexpr ( // NOLINT
107+
rclcpp::function_traits::same_arguments<
108+
CallbackT,
109+
ConstRefWithRequestHeaderCallback
110+
>::value)
111+
{
112+
callback_.template emplace<ConstRefWithRequestHeaderCallback>(callback);
99113
} else {
100114
// the else clause is not needed, but anyways we should only be doing this instead
101115
// of all the above workaround ...
@@ -141,6 +155,20 @@ class AnyServiceCallback
141155
>::value)
142156
{
143157
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
158+
} else if constexpr ( // NOLINT
159+
rclcpp::function_traits::same_arguments<
160+
CallbackT,
161+
ConstRefCallback
162+
>::value)
163+
{
164+
callback_.template emplace<ConstRefCallback>(callback);
165+
} else if constexpr ( // NOLINT
166+
rclcpp::function_traits::same_arguments<
167+
CallbackT,
168+
ConstRefWithRequestHeaderCallback
169+
>::value)
170+
{
171+
callback_.template emplace<ConstRefWithRequestHeaderCallback>(callback);
144172
} else {
145173
// the else clause is not needed, but anyways we should only be doing this instead
146174
// of all the above workaround ...
@@ -182,6 +210,12 @@ class AnyServiceCallback
182210
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
183211
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
184212
cb(request_header, std::move(request), response);
213+
} else if (std::holds_alternative<ConstRefCallback>(callback_)) {
214+
const auto & cb = std::get<ConstRefCallback>(callback_);
215+
cb(*request, *response);
216+
} else if (std::holds_alternative<ConstRefWithRequestHeaderCallback>(callback_)) {
217+
const auto & cb = std::get<ConstRefWithRequestHeaderCallback>(callback_);
218+
cb(*request_header, *request, *response);
185219
}
186220
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
187221
return response;
@@ -227,13 +261,28 @@ class AnyServiceCallback
227261
std::shared_ptr<rmw_request_id_t>,
228262
std::shared_ptr<typename ServiceT::Request>
229263
)>;
264+
// The request and response are received as shared pointers and dereferenced before
265+
// calling the user callback, like AnySubscriptionCallback does for `const MessageT &`.
266+
using ConstRefCallback = std::function<
267+
void (
268+
const typename ServiceT::Request &,
269+
typename ServiceT::Response &
270+
)>;
271+
using ConstRefWithRequestHeaderCallback = std::function<
272+
void (
273+
const rmw_request_id_t &,
274+
const typename ServiceT::Request &,
275+
typename ServiceT::Response &
276+
)>;
230277

231278
std::variant<
232279
std::monostate,
233280
SharedPtrCallback,
234281
SharedPtrWithRequestHeaderCallback,
235282
SharedPtrDeferResponseCallback,
236-
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
283+
SharedPtrDeferResponseCallbackWithServiceHandle,
284+
ConstRefCallback,
285+
ConstRefWithRequestHeaderCallback> callback_;
237286
};
238287

239288
} // namespace rclcpp

rclcpp/test/rclcpp/test_any_service_callback.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "rclcpp/any_service_callback.hpp"
2525
#include "rclcpp/service.hpp"
26+
#include "test_msgs/srv/basic_types.hpp"
2627
#include "test_msgs/srv/empty.hpp"
2728

2829
class TestAnyServiceCallback : public ::testing::Test
@@ -109,3 +110,91 @@ TEST_F(TestAnyServiceCallback, set_and_dispatch_defered_with_service_handle) {
109110
EXPECT_EQ(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
110111
EXPECT_EQ(callback_with_header_calls, 1);
111112
}
113+
114+
TEST_F(TestAnyServiceCallback, set_and_dispatch_const_ref_no_header) {
115+
int callback_calls = 0;
116+
auto callback = [&callback_calls](
117+
const test_msgs::srv::Empty::Request &,
118+
test_msgs::srv::Empty::Response &)
119+
{
120+
callback_calls++;
121+
};
122+
123+
any_service_callback_.set(callback);
124+
EXPECT_NO_THROW(
125+
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
126+
EXPECT_EQ(callback_calls, 1);
127+
}
128+
129+
TEST_F(TestAnyServiceCallback, set_and_dispatch_const_ref_header) {
130+
int callback_calls = 0;
131+
int64_t seen_sequence_number = 0;
132+
request_header_->sequence_number = 42;
133+
auto callback = [&callback_calls, &seen_sequence_number](
134+
const rmw_request_id_t & request_header,
135+
const test_msgs::srv::Empty::Request &,
136+
test_msgs::srv::Empty::Response &)
137+
{
138+
callback_calls++;
139+
seen_sequence_number = request_header.sequence_number;
140+
};
141+
142+
any_service_callback_.set(callback);
143+
EXPECT_NO_THROW(
144+
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
145+
EXPECT_EQ(callback_calls, 1);
146+
EXPECT_EQ(seen_sequence_number, 42);
147+
}
148+
149+
TEST_F(TestAnyServiceCallback, const_ref_response_is_returned) {
150+
rclcpp::AnyServiceCallback<test_msgs::srv::BasicTypes> any_service_callback;
151+
auto request = std::make_shared<test_msgs::srv::BasicTypes::Request>();
152+
request->int64_value = 7;
153+
request->string_value = "ping";
154+
155+
auto callback = [](
156+
const test_msgs::srv::BasicTypes::Request & req,
157+
test_msgs::srv::BasicTypes::Response & res)
158+
{
159+
res.int64_value = req.int64_value * 2;
160+
res.string_value = req.string_value + "-pong";
161+
};
162+
163+
any_service_callback.set(callback);
164+
auto response = any_service_callback.dispatch(nullptr, request_header_, request);
165+
ASSERT_NE(nullptr, response);
166+
EXPECT_EQ(response->int64_value, 14);
167+
EXPECT_EQ(response->string_value, "ping-pong");
168+
}
169+
170+
TEST_F(TestAnyServiceCallback, set_and_dispatch_const_ref_std_bind) {
171+
struct Handler
172+
{
173+
int calls = 0;
174+
void no_header(const test_msgs::srv::Empty::Request &, test_msgs::srv::Empty::Response &)
175+
{
176+
calls++;
177+
}
178+
void header(
179+
const rmw_request_id_t &,
180+
const test_msgs::srv::Empty::Request &,
181+
test_msgs::srv::Empty::Response &)
182+
{
183+
calls++;
184+
}
185+
};
186+
Handler handler;
187+
188+
any_service_callback_.set(
189+
std::bind(&Handler::no_header, &handler, std::placeholders::_1, std::placeholders::_2));
190+
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_));
191+
192+
rclcpp::AnyServiceCallback<test_msgs::srv::Empty> any_service_callback_with_header;
193+
any_service_callback_with_header.set(
194+
std::bind(
195+
&Handler::header, &handler,
196+
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
197+
EXPECT_NE(
198+
nullptr, any_service_callback_with_header.dispatch(nullptr, request_header_, request_));
199+
EXPECT_EQ(handler.calls, 2);
200+
}

rclcpp/test/rclcpp/test_service.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,26 @@ TEST_F(TestService, server_qos_depth) {
414414

415415
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
416416
}
417+
418+
TEST_F(TestService, const_ref_callback) {
419+
uint64_t server_cb_count = 0;
420+
auto server_callback = [&server_cb_count](
421+
const test_msgs::srv::Empty::Request &,
422+
test_msgs::srv::Empty::Response &) {server_cb_count++;};
423+
424+
auto server = node->create_service<test_msgs::srv::Empty>(
425+
"test_const_ref_callback", std::move(server_callback));
426+
auto client = node->create_client<test_msgs::srv::Empty>("test_const_ref_callback");
427+
428+
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
429+
auto future = client->async_send_request(request);
430+
431+
rclcpp::executors::SingleThreadedExecutor executor;
432+
executor.add_node(node);
433+
EXPECT_EQ(
434+
executor.spin_until_future_complete(future, 10s),
435+
rclcpp::FutureReturnCode::SUCCESS);
436+
437+
EXPECT_EQ(server_cb_count, 1u);
438+
EXPECT_NE(nullptr, future.get());
439+
}

0 commit comments

Comments
 (0)