nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
service.hpp
Go to the documentation of this file.
1// nros-cpp: the DISPATCH service server
2// Freestanding C++ — no exceptions, no STL required
3
16#ifndef NROS_RCLCPP_SERVICE_HPP
17#define NROS_RCLCPP_SERVICE_HPP
18
19#include <cstdint>
20#include <cstddef>
21
22#include "rclcpp/detail/callback_context.hpp" // phase-456 W3 — the handler IS the arena context
24#include "rclcpp/detail/entity_name.hpp" // phase-444 — the one entity-name copy
25#include "rclcpp/result.hpp"
26#include "rclcpp/service_handle.hpp" // phase-456 W5 — what `Service<S>::SharedPtr` IS
27#include "rclcpp/size_bound.hpp" // rclcpp::rx_buffer_capacity<M> — the receive-buffer size
28
29#include "rclcpp/detail/nros_cpp_ffi.h"
30
31// issue 1437 — the two granted-QoS accessors return a `rclcpp::QoS` BY VALUE from
32// an inline body, so the complete type must be here, not only by the time
33// `rclcpp/node.hpp` is pulled in below.
34//
35// AFTER `nros_cpp_ffi.h`, never before: `qos.hpp` defines the four
36// `nros_cpp_qos_*_t` enums ITSELF under `#ifndef NROS_CPP_FFI_H`, so
37// reaching it first makes the cbindgen header a REDEFINITION of all four.
38#include "rclcpp/qos.hpp"
39
40// Phase 189.M3.3.e — `nros_cpp_service_server_register` is excluded from
41// cbindgen (its Rust signature uses `RawServiceCallback`, an external-crate
42// type alias cbindgen names without defining). Declare it locally with a plain
43// function-pointer typedef matching the ABI (`bool(req, req_len, resp,
44// resp_cap, resp_len, ctx)`).
45extern "C" {
46typedef bool (*nros_cpp_service_request_callback_t)(const uint8_t* req, size_t req_len,
47 uint8_t* resp, size_t resp_cap,
48 size_t* resp_len, void* ctx);
49
51 const char* service_name, const char* type_name,
52 const char* type_hash, nros_cpp_qos_t qos,
54 void* context, uint8_t sched_context,
55 size_t* out_handle_id);
56} // extern "C"
57
65namespace rclcpp {
66class Node;
67}
68
69// ============================================================================
70// `rclcpp::Service<S>` -- DEFINED here (RFC-0089: rclcpp:: is the home)
71// ============================================================================
72//
73// phase-428: the definition moved from `nros::` to `rclcpp::` (the `nros::`
74// alias went in phase-483 W1). The nested `SharedPtr` / `ConstSharedPtr` / `UniquePtr`
75// aliases live on the class itself, so the rclcpp way of indexing types
76// (`rclcpp::Service<S>::SharedPtr`) resolves with no wrapper in between.
77namespace rclcpp {
78
118template <typename S> class Service {
119 public:
137
138 using RequestType = typename S::Request;
139 using ResponseType = typename S::Response;
140
153
155 bool is_valid() const { return initialized_; }
156
171 const char* get_service_name() const { return initialized_ ? service_name_ : ""; }
172
197 ::rclcpp::QoS get_request_subscription_actual_qos() const { return actual_qos_half(true); }
198
202 ::rclcpp::QoS get_response_publisher_actual_qos() const { return actual_qos_half(false); }
203
213 ~Service() { initialized_ = false; }
214
215 // Move semantics (non-copyable). Bookkeeping only — there is no storage to
216 // relocate, so `nros_cpp_service_server_relocate` is not called here.
217 //
218 // phase-456 W3 — a callback-style service is MOVABLE, and the warning that
219 // used to stand here is gone with its subject. It said:
220 //
221 // A callback-style service must NOT be moved after register — the arena
222 // holds `this` as the trampoline context (Phase 189.M3.3.e); the move
223 // only transfers bookkeeping and leaves that pointer stale, so don't.
224 //
225 // The arena holds the user's HANDLER as its context now, not `this`, so
226 // after registration nothing of the caller's is referenced and there is no
227 // pointer a move could leave stale. What moves is bookkeeping, which is
228 // what the warning said it was — the difference is that bookkeeping is now
229 // all there is.
231 : initialized_(other.initialized_), handle_id_(other.handle_id_), service_name_{},
232 executor_(other.executor_) {
233 ::rclcpp::detail::assign_entity_name(service_name_, other.service_name_);
234 other.initialized_ = false;
235 }
236
238 if (this != &other) {
239 initialized_ = other.initialized_;
240 handle_id_ = other.handle_id_;
241 ::rclcpp::detail::assign_entity_name(service_name_, other.service_name_);
242 // issue 1437 — moves with the index it pairs with.
243 executor_ = other.executor_;
244 other.initialized_ = false;
245 }
246 return *this;
247 }
248
251 Service() : initialized_(false), service_name_{} {}
252
254 size_t handle_id() const { return handle_id_; }
255
256 private:
257 Service(const Service&) = delete;
258 Service& operator=(const Service&) = delete;
259
260 friend class ::rclcpp::Node;
261
271 static bool request_trampoline(const uint8_t* req, size_t req_len, uint8_t* resp,
272 size_t resp_cap, size_t* resp_len, void* ctx) {
273 const TypedServiceFn user_fn = ::rclcpp::detail::fn_from_context<TypedServiceFn>(ctx);
274 if (user_fn == nullptr) return false;
276 if (RequestType::ffi_deserialize(req, req_len, &request) != 0) return false;
279 size_t len = 0;
280 if (ResponseType::ffi_serialize(&response, resp, resp_cap, &len) != 0) return false;
281 *resp_len = len;
282 return true;
283 }
284
293 ::rclcpp::QoS actual_qos_half(bool request) const {
295 nros_cpp_qos_t resp{};
296 if (!initialized_) return ::rclcpp::detail::qos_all_unknown();
297 if (nros_cpp_service_server_get_actual_qos(nullptr, executor_, handle_id_, &req, &resp) !=
298 0) {
299 return ::rclcpp::detail::qos_all_unknown();
300 }
301 return ::rclcpp::detail::qos_from_ffi(request ? req : resp);
302 }
303
304 bool initialized_;
305 // Callback-style BOOKKEEPING (Phase 189.M3.3.e). The handler itself is not
306 // here — it lives in the arena (phase-456 W3) — and neither is the server,
307 // so what remains is the caller's own record of a registration that refers
308 // back to nothing of the caller's.
309 size_t handle_id_ = static_cast<size_t>(-1);
310
313 char service_name_[::rclcpp::SERVICE_NAME_MAX];
314
315 // issue 1437 — the executor whose arena holds this service, recorded with
316 // the index that names it there. `handle_id_` alone points into an arena
317 // this object could not otherwise reach.
318 void* executor_ = nullptr;
319};
320
321} // namespace rclcpp
322
323// Phase 84.G8: out-of-line definition of Node::create_service<S>().
324#include "rclcpp/node.hpp"
325
326namespace rclcpp {
327
328// Phase 189.M3.3.e — callback-style (arena-registered) service. The arena owns
329// the server AND the request handler, and dispatches it during spin_once, so
330// the handle is real and `options.sched_context` is functional. `out` receives
331// the handle id and nothing the arena refers back to (phase-456 W3).
332} // namespace rclcpp
333
334namespace rclcpp {
335template <typename S, typename F, typename>
336Result Node::create_service(Service<S>& out, const char* service_name, F callback,
337 const ::rclcpp::QoS& qos, const ::rclcpp::ServiceOptions& options) {
338 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
339 nros_cpp_qos_t ffi_qos = ::rclcpp::detail::qos_to_ffi(qos);
340
341 // The user handler becomes the ARENA's context (phase-456 W3). The
342 // conversion is still the compile error for a non-convertible `F`; what
343 // changed is where the resulting pointer is stored — in the registration,
344 // not in `out`, so `out` is no longer an object the arena knows by address.
345 const typename Service<S>::TypedServiceFn user_fn =
346 typename Service<S>::TypedServiceFn(callback);
347
348 uint8_t sched = (options.sched_context == ::rclcpp::SCHED_CONTEXT_UNSET)
349 ? 0u
350 : static_cast<uint8_t>(options.sched_context);
351 size_t handle = static_cast<size_t>(-1);
353 &handle_, service_name, S::TYPE_NAME, S::Request::TYPE_HASH, ffi_qos,
354 reinterpret_cast<nros_cpp_service_request_callback_t>(&Service<S>::request_trampoline),
355 ::rclcpp::detail::fn_to_context(user_fn), sched, &handle);
356 if (ret == 0) {
357 out.handle_id_ = handle;
358 // phase-444 — remember the name for `get_service_name()`; the runtime
359 // takes `service_name` and drops it.
360 ::rclcpp::detail::assign_entity_name(out.service_name_, service_name);
361
362 // issue 1437 — the arena that owns the entity, beside its index.
363 out.executor_ = executor_handle_;
364 out.initialized_ = true;
365 }
366 return Result(ret);
367}
368} // namespace rclcpp
369
370namespace rclcpp {} // namespace rclcpp
371
372#endif // NROS_RCLCPP_SERVICE_HPP
Definition client.hpp:129
::rclcpp::Owned<::rclcpp::PollService< S > > create_service(const char *name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
Definition qos.hpp:237
Definition service.hpp:118
const char * get_service_name() const
Definition service.hpp:171
Service & operator=(Service &&other)
Definition service.hpp:237
Service(Service &&other)
Definition service.hpp:230
~Service()
Definition service.hpp:213
void(*)(const RequestType &request, ResponseType &response) TypedServiceFn
Definition service.hpp:152
size_t handle_id() const
Executor arena slot for the registration; SIZE_MAX until registered.
Definition service.hpp:254
::rclcpp::QoS get_response_publisher_actual_qos() const
Definition service.hpp:202
typename S::Request RequestType
Definition service.hpp:138
::rclcpp::QoS get_request_subscription_actual_qos() const
Definition service.hpp:197
bool is_valid() const
Check if the service was registered.
Definition service.hpp:155
Service()
Definition service.hpp:251
typename S::Response ResponseType
Definition service.hpp:139
Inline storage-size macros for opaque entity buffers.
int nros_cpp_ret_t
Definition future.hpp:21
Definition rclcpp.hpp:75
ResultOf< void > Result
Definition result.hpp:246
rclcpp::Node and global session helpers.
rclcpp::QoS — full DDS-shaped QoS settings (Phase 108.B.7).
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
bool(* nros_cpp_service_request_callback_t)(const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_cap, size_t *resp_len, void *ctx)
Definition service.hpp:46
nros_cpp_ret_t nros_cpp_service_server_register(const nros_cpp_node_t *node, const char *service_name, const char *type_name, const char *type_hash, nros_cpp_qos_t qos, nros_cpp_service_request_callback_t callback, void *context, uint8_t sched_context, size_t *out_handle_id)
Definition qos.hpp:81