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
15
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
57// phase-488 — the capturing twin, excluded from cbindgen for the same reason.
59 const nros_cpp_node_t* node, const char* service_name, const char* type_name,
60 const char* type_hash, nros_cpp_qos_t qos, nros_cpp_service_request_callback_t callback,
61 const uint8_t* capture, size_t capture_len, void (*capture_drop)(void*), uint8_t sched_context,
62 size_t* out_handle_id);
63} // extern "C"
64
65// `rmw_request_id_t` — upstream's request header. Upstream's three-argument
66// service handler receives it; issue 2081 fills it from the backend's report.
67#include "rclcpp/detail/rmw_request_id.hpp"
68
69extern "C" {
71typedef bool (*nros_cpp_service_request_id_callback_t)(const uint8_t* req, size_t req_len,
72 rmw_request_id_t* request_id, uint8_t* resp,
73 size_t resp_cap, size_t* resp_len,
74 void* ctx);
75
76// Issue 2081 — the request-id registration, excluded from cbindgen for the same
77// reason. A non-NULL `capture` is copied into the arena (as the capturing
78// form); otherwise `context` is handed back.
80 const nros_cpp_node_t* node, const char* service_name, const char* type_name,
81 const char* type_hash, nros_cpp_qos_t qos, nros_cpp_service_request_id_callback_t callback,
82 void* context, const uint8_t* capture, size_t capture_len, void (*capture_drop)(void*),
83 uint8_t sched_context, size_t* out_handle_id);
84} // extern "C"
85
93namespace rclcpp {
94class Node;
95}
96
97// ============================================================================
98// `rclcpp::Service<S>` -- DEFINED here (RFC-0089: rclcpp:: is the home)
99// ============================================================================
100//
101// phase-428: the definition moved from `nros::` to `rclcpp::` (the `nros::`
102// alias went in phase-483 W1). The nested `SharedPtr` / `ConstSharedPtr` / `UniquePtr`
103// aliases live on the class itself, so the rclcpp way of indexing types
104// (`rclcpp::Service<S>::SharedPtr`) resolves with no wrapper in between.
105namespace rclcpp {
106
146template <typename S> class Service {
147 public:
158 using SharedPtr = ::rclcpp::ServiceHandle<S>;
162 using ConstSharedPtr = ::rclcpp::ServiceHandle<S>;
164 using UniquePtr = ::rclcpp::ServiceHandle<S>;
165
166 using RequestType = typename S::Request;
167 using ResponseType = typename S::Response;
168
180 using TypedServiceFn = void (*)(const RequestType& request, ResponseType& response);
181
183 bool is_valid() const { return initialized_; }
184
199 const char* get_service_name() const { return initialized_ ? service_name_ : ""; }
200
225 ::rclcpp::QoS get_request_subscription_actual_qos() const { return actual_qos_half(true); }
226
230 ::rclcpp::QoS get_response_publisher_actual_qos() const { return actual_qos_half(false); }
231
241 ~Service() { initialized_ = false; }
242
243 // Move semantics (non-copyable). Bookkeeping only — there is no storage to
244 // relocate, so `nros_cpp_service_server_relocate` is not called here.
245 //
246 // phase-456 W3 — a callback-style service is MOVABLE, and the warning that
247 // used to stand here is gone with its subject. It said:
248 //
249 // A callback-style service must NOT be moved after register — the arena
250 // holds `this` as the trampoline context (Phase 189.M3.3.e); the move
251 // only transfers bookkeeping and leaves that pointer stale, so don't.
252 //
253 // The arena holds the user's HANDLER as its context now, not `this`, so
254 // after registration nothing of the caller's is referenced and there is no
255 // pointer a move could leave stale. What moves is bookkeeping, which is
256 // what the warning said it was — the difference is that bookkeeping is now
257 // all there is.
259 : initialized_(other.initialized_), handle_id_(other.handle_id_), service_name_{},
260 executor_(other.executor_) {
261 ::rclcpp::detail::assign_entity_name(service_name_, other.service_name_);
262 other.initialized_ = false;
263 }
264
266 if (this != &other) {
267 initialized_ = other.initialized_;
268 handle_id_ = other.handle_id_;
269 ::rclcpp::detail::assign_entity_name(service_name_, other.service_name_);
270 // issue 1437 — moves with the index it pairs with.
271 executor_ = other.executor_;
272 other.initialized_ = false;
273 }
274 return *this;
275 }
276
279 Service() : initialized_(false), service_name_{} {}
280
282 size_t handle_id() const { return handle_id_; }
283
284 private:
285 Service(const Service&) = delete;
286 Service& operator=(const Service&) = delete;
287
288 friend class ::rclcpp::Node;
289
299 static bool request_trampoline(const uint8_t* req, size_t req_len, uint8_t* resp,
300 size_t resp_cap, size_t* resp_len, void* ctx) {
301 const TypedServiceFn user_fn = ::rclcpp::detail::fn_from_context<TypedServiceFn>(ctx);
302 if (user_fn == nullptr) return false;
303 RequestType request;
304 if (RequestType::ffi_deserialize(req, req_len, &request) != 0) return false;
305 ResponseType response;
306 user_fn(request, response);
307 size_t len = 0;
308 if (ResponseType::ffi_serialize(&response, resp, resp_cap, &len) != 0) return false;
309 *resp_len = len;
310 return true;
311 }
312
321 ::rclcpp::QoS actual_qos_half(bool request) const {
322 nros_cpp_qos_t req{};
323 nros_cpp_qos_t resp{};
324 if (!initialized_) return ::rclcpp::detail::qos_all_unknown();
325 if (nros_cpp_service_server_get_actual_qos(nullptr, executor_, handle_id_, &req, &resp) !=
326 0) {
327 return ::rclcpp::detail::qos_all_unknown();
328 }
329 return ::rclcpp::detail::qos_from_ffi(request ? req : resp);
330 }
331
332 bool initialized_;
333 // Callback-style BOOKKEEPING (Phase 189.M3.3.e). The handler itself is not
334 // here — it lives in the arena (phase-456 W3) — and neither is the server,
335 // so what remains is the caller's own record of a registration that refers
336 // back to nothing of the caller's.
337 size_t handle_id_ = static_cast<size_t>(-1);
338
341 char service_name_[::rclcpp::SERVICE_NAME_MAX];
342
343 // issue 1437 — the executor whose arena holds this service, recorded with
344 // the index that names it there. `handle_id_` alone points into an arena
345 // this object could not otherwise reach.
346 void* executor_ = nullptr;
347};
348
349} // namespace rclcpp
350
351// Phase 84.G8: out-of-line definition of Node::create_service<S>().
352#include "rclcpp/node.hpp"
353
354namespace rclcpp {
355
356// Phase 189.M3.3.e — callback-style (arena-registered) service. The arena owns
357// the server AND the request handler, and dispatches it during spin_once, so
358// the handle is real and `options.sched_context` is functional. `out` receives
359// the handle id and nothing the arena refers back to (phase-456 W3).
360} // namespace rclcpp
361
362namespace rclcpp {
363template <typename S, typename F, typename>
364Result Node::create_service(Service<S>& out, const char* service_name, F callback,
365 const ::rclcpp::QoS& qos, const ::rclcpp::ServiceOptions& options) {
366 // The user handler becomes the ARENA's context (phase-456 W3). The
367 // conversion is still the compile error for a non-convertible `F`; what
368 // changed is where the resulting pointer is stored — in the registration,
369 // not in `out`, so `out` is no longer an object the arena knows by address.
370 const typename Service<S>::TypedServiceFn user_fn =
371 typename Service<S>::TypedServiceFn(callback);
372 return this->template register_service_<S>(out, service_name, &Service<S>::request_trampoline,
373 ::rclcpp::detail::fn_to_context(user_fn), qos,
374 options);
375}
376
377template <typename S>
378Result Node::register_service_(Service<S>& out, const char* service_name,
379 bool (*trampoline)(const uint8_t*, size_t, uint8_t*, size_t, size_t*,
380 void*),
381 void* context, const ::rclcpp::QoS& qos,
382 const ::rclcpp::ServiceOptions& options, const void* capture,
383 size_t capture_len, void (*capture_drop)(void*)) {
384 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
386
387 uint8_t sched = (options.sched_context == ::rclcpp::SCHED_CONTEXT_UNSET)
388 ? 0u
389 : static_cast<uint8_t>(options.sched_context);
390 size_t handle = static_cast<size_t>(-1);
391 nros_cpp_ret_t ret =
392 (capture != nullptr && capture_len != 0)
394 &handle_, service_name, S::TYPE_NAME, S::Request::TYPE_HASH, ffi_qos, trampoline,
395 static_cast<const uint8_t*>(capture), capture_len, capture_drop, sched, &handle)
396 : nros_cpp_service_server_register(&handle_, service_name, S::TYPE_NAME,
397 S::Request::TYPE_HASH, ffi_qos, trampoline, context,
398 sched, &handle);
399 return this->template finish_register_service_<S>(out, service_name, ret, handle);
400}
401
402// Issue 2081 — the same registration for a trampoline that also receives the
403// request's id (the hosted `shared_ptr` handlers, whose three-argument form
404// hands it to the user as the request header).
405template <typename S>
406Result Node::register_service_(Service<S>& out, const char* service_name,
407 nros_cpp_service_request_id_callback_t trampoline, void* context,
408 const ::rclcpp::QoS& qos, const ::rclcpp::ServiceOptions& options,
409 const void* capture, size_t capture_len,
410 void (*capture_drop)(void*)) {
411 if (!initialized_) return Result(::rclcpp::ErrorCode::NotInitialized);
413 uint8_t sched = (options.sched_context == ::rclcpp::SCHED_CONTEXT_UNSET)
414 ? 0u
415 : static_cast<uint8_t>(options.sched_context);
416 size_t handle = static_cast<size_t>(-1);
417 const bool captured = capture != nullptr && capture_len != 0;
419 &handle_, service_name, S::TYPE_NAME, S::Request::TYPE_HASH, ffi_qos, trampoline,
420 captured ? nullptr : context, captured ? static_cast<const uint8_t*>(capture) : nullptr,
421 captured ? capture_len : 0, captured ? capture_drop : nullptr, sched, &handle);
422 return this->template finish_register_service_<S>(out, service_name, ret, handle);
423}
424
425template <typename S>
426Result Node::finish_register_service_(Service<S>& out, const char* service_name, nros_cpp_ret_t ret,
427 size_t handle) {
428 if (ret == 0) {
429 out.handle_id_ = handle;
430 // phase-444 — remember the name for `get_service_name()`; the runtime
431 // takes `service_name` and drops it.
432 ::rclcpp::detail::assign_entity_name(out.service_name_, service_name);
433
434 // issue 1437 — the arena that owns the entity, beside its index.
435 out.executor_ = executor_handle_;
436 out.initialized_ = true;
437 }
438 return Result(ret);
439}
440} // namespace rclcpp
441
442#include "rclcpp/logging.hpp" // phase-488 — NROS_RCLCPP_SAY_REFUSED for the shared_ptr handler
443#include "rclcpp/inplace_fn.hpp" // phase-488 — a stateful handler's arena capture
444
445#if defined(NROS_CPP_HAS_SHARED_PTR) // hosted-family: shared-ptr-interop
446namespace rclcpp {
447namespace detail {
448
465struct FrameBlock {
470 static const size_t CAPACITY = 96;
471 alignas(alignof(::std::max_align_t)) unsigned char bytes[CAPACITY];
472 bool used = false;
473};
474
475[[noreturn]] inline void abort_frame_block() {
476 RCLC_LOG_ERROR(log_handle(nullptr),
477 "rclcpp: a shared_ptr control block does not fit FrameBlock::CAPACITY");
478 ::std::abort();
479}
480
481template <typename T> struct FrameBlockAllocator {
482 using value_type = T;
483 FrameBlock* block;
484 explicit FrameBlockAllocator(FrameBlock* b) : block(b) {}
485 template <typename U> FrameBlockAllocator(const FrameBlockAllocator<U>& o) : block(o.block) {}
486 T* allocate(size_t n) {
487 if (block->used || n * sizeof(T) > FrameBlock::CAPACITY ||
488 alignof(T) > alignof(::std::max_align_t)) {
489 abort_frame_block();
490 }
491 block->used = true;
492 return reinterpret_cast<T*>(block->bytes);
493 }
494 void deallocate(T*, size_t) { block->used = false; }
495 template <typename U> bool operator==(const FrameBlockAllocator<U>& o) const {
496 return block == o.block;
497 }
498 template <typename U> bool operator!=(const FrameBlockAllocator<U>& o) const {
499 return block != o.block;
500 }
501};
502
503struct LendDeleter {
504 template <typename T> void operator()(T*) const {}
505};
506
507template <typename T> inline ::std::shared_ptr<T> lend_from_frame(T* object, FrameBlock* block) {
508 return ::std::shared_ptr<T>(object, LendDeleter(), FrameBlockAllocator<T>(block));
509}
510
511template <typename S> struct shared_ptr_service_fn {
512 using Request = typename S::Request;
513 using Response = typename S::Response;
514 using two = void (*)(::std::shared_ptr<Request>, ::std::shared_ptr<Response>);
515 using three = void (*)(::std::shared_ptr<::rmw_request_id_t>, ::std::shared_ptr<Request>,
516 ::std::shared_ptr<Response>);
517};
518
521template <typename S, typename F> struct service_ptr_shape {
522 static const int value =
523 ::rclcpp::tr::is_convertible<F, typename shared_ptr_service_fn<S>::three>::value ? 3
524 : ::rclcpp::tr::is_convertible<F, typename shared_ptr_service_fn<S>::two>::value ? 2
525 : 0;
526};
527
533template <typename S, typename Invoke>
534inline bool serve_lent(const uint8_t* req, size_t req_len, const ::rmw_request_id_t* id,
535 uint8_t* resp, size_t resp_cap, size_t* resp_len, const Invoke& invoke) {
536 typename S::Request request;
537 if (S::Request::ffi_deserialize(req, req_len, &request) != 0) return false;
538 typename S::Response response;
539 // Issue 2081 — the id the backend reported for this request (all zero
540 // when it reported none).
541 ::rmw_request_id_t header = {};
542 if (id != nullptr) header = *id;
543 {
544 FrameBlock request_block;
545 FrameBlock response_block;
546 FrameBlock header_block;
547 ::std::shared_ptr<typename S::Request> request_ptr =
548 lend_from_frame(&request, &request_block);
549 ::std::shared_ptr<typename S::Response> response_ptr =
550 lend_from_frame(&response, &response_block);
551 ::std::shared_ptr<::rmw_request_id_t> header_ptr = lend_from_frame(&header, &header_block);
552 invoke(header_ptr, request_ptr, response_ptr);
553 if (request_ptr.use_count() != 1 || response_ptr.use_count() != 1 ||
554 header_ptr.use_count() != 1) {
555 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_RETAINED_SERVICE_PTR);
556 ::std::abort();
557 }
558 }
559 size_t len = 0;
560 if (S::Response::ffi_serialize(&response, resp, resp_cap, &len) != 0) return false;
561 *resp_len = len;
562 return true;
563}
564
565template <typename S, int Shape> struct shared_ptr_service_trampoline {
566 static bool call(const uint8_t* req, size_t req_len, ::rmw_request_id_t* id, uint8_t* resp,
567 size_t resp_cap, size_t* resp_len, void* ctx) {
568 using Fn = shared_ptr_service_fn<S>;
569 return serve_lent<S>(req, req_len, id, resp, resp_cap, resp_len,
570 [ctx](::std::shared_ptr<::rmw_request_id_t>& h,
571 ::std::shared_ptr<typename S::Request>& rq,
572 ::std::shared_ptr<typename S::Response>& rs) {
573 if (Shape == 3) {
574 fn_from_context<typename Fn::three>(ctx)(h, rq, rs);
575 } else {
576 (void)h;
577 fn_from_context<typename Fn::two>(ctx)(rq, rs);
578 }
579 });
580 }
581};
582
591template <typename S, typename F> class stateful_service_shape {
592 using Rq = typename S::Request;
593 using Rs = typename S::Response;
594 using H = ::std::shared_ptr<::rmw_request_id_t>;
595 template <typename G>
596 static auto probe3(int)
597 -> decltype(::std::declval<G&>()(::std::declval<H&>(),
598 ::std::declval<::std::shared_ptr<Rq>&>(),
599 ::std::declval<::std::shared_ptr<Rs>&>()),
600 char(0)) {
601 return 0;
602 }
603 template <typename G> static long probe3(...) { return 0; }
604 template <typename G>
605 static auto probe2(int)
606 -> decltype(::std::declval<G&>()(::std::declval<::std::shared_ptr<Rq>&>(),
607 ::std::declval<::std::shared_ptr<Rs>&>()),
608 char(0)) {
609 return 0;
610 }
611 template <typename G> static long probe2(...) { return 0; }
612 template <typename G>
613 static auto probe1(int)
614 -> decltype(::std::declval<G&>()(::std::declval<const Rq&>(), ::std::declval<Rs&>()),
615 char(0)) {
616 return 0;
617 }
618 template <typename G> static long probe1(...) { return 0; }
619
620 public:
621 static const int value = sizeof(probe3<F>(0)) == sizeof(char) ? 3
622 : sizeof(probe2<F>(0)) == sizeof(char) ? 2
623 : sizeof(probe1<F>(0)) == sizeof(char) ? 1
624 : 0;
625};
626
632template <typename S>
633using StatefulServiceFn = ::rclcpp::InplaceFn<void(::std::shared_ptr<::rmw_request_id_t>&,
634 ::std::shared_ptr<typename S::Request>&,
635 ::std::shared_ptr<typename S::Response>&),
636 8 * sizeof(void*)>;
637
638template <typename S, int Shape> struct stateful_service_adapter;
639template <typename S> struct stateful_service_adapter<S, 3> {
640 template <typename F> static StatefulServiceFn<S> wrap(F f) {
641 return StatefulServiceFn<S>(
642 [f](::std::shared_ptr<::rmw_request_id_t>& h,
643 ::std::shared_ptr<typename S::Request>& rq,
644 ::std::shared_ptr<typename S::Response>& rs) mutable { (void)f(h, rq, rs); });
645 }
646};
647template <typename S> struct stateful_service_adapter<S, 2> {
648 template <typename F> static StatefulServiceFn<S> wrap(F f) {
649 return StatefulServiceFn<S>(
650 [f](::std::shared_ptr<::rmw_request_id_t>&, ::std::shared_ptr<typename S::Request>& rq,
651 ::std::shared_ptr<typename S::Response>& rs) mutable { (void)f(rq, rs); });
652 }
653};
654template <typename S> struct stateful_service_adapter<S, 1> {
655 template <typename F> static StatefulServiceFn<S> wrap(F f) {
656 return StatefulServiceFn<S>([f](::std::shared_ptr<::rmw_request_id_t>&,
657 ::std::shared_ptr<typename S::Request>& rq,
658 ::std::shared_ptr<typename S::Response>& rs) mutable {
659 (void)f(static_cast<const typename S::Request&>(*rq), *rs);
660 });
661 }
662};
663
665template <typename S> struct stateful_service_trampoline {
666 static bool call(const uint8_t* req, size_t req_len, ::rmw_request_id_t* id, uint8_t* resp,
667 size_t resp_cap, size_t* resp_len, void* ctx) {
668 const StatefulServiceFn<S>* fn = static_cast<const StatefulServiceFn<S>*>(ctx);
669 return serve_lent<S>(req, req_len, id, resp, resp_cap, resp_len, *fn);
670 }
671 static void destroy(void* ctx) { static_cast<StatefulServiceFn<S>*>(ctx)->clear(); }
672};
673
676template <typename S, typename F>
677inline void* shared_ptr_service_context(F callback, ::rclcpp::tr::integral_constant<int, 3>) {
678 return fn_to_context(typename shared_ptr_service_fn<S>::three(callback));
679}
680template <typename S, typename F>
681inline void* shared_ptr_service_context(F callback, ::rclcpp::tr::integral_constant<int, 2>) {
682 return fn_to_context(typename shared_ptr_service_fn<S>::two(callback));
683}
684
685} // namespace detail
686} // namespace rclcpp
687#endif // NROS_CPP_HAS_SHARED_PTR
688
689namespace rclcpp {} // namespace rclcpp
690
691#endif // NROS_RCLCPP_SERVICE_HPP
Definition node.hpp:845
::rclcpp::Owned<::rclcpp::PollService< S > > create_service(const char *name, const ::rclcpp::QoS &qos=::rclcpp::QoS::services())
Definition qos.hpp:276
Definition service.hpp:146
const char * get_service_name() const
Definition service.hpp:199
Service & operator=(Service &&other)
Definition service.hpp:265
Service(Service &&other)
Definition service.hpp:258
~Service()
Definition service.hpp:241
void(*)(const RequestType &request, ResponseType &response) TypedServiceFn
Definition service.hpp:180
size_t handle_id() const
Executor arena slot for the registration; SIZE_MAX until registered.
Definition service.hpp:282
::rclcpp::QoS get_response_publisher_actual_qos() const
Definition service.hpp:230
typename S::Request RequestType
Definition service.hpp:166
::rclcpp::QoS get_request_subscription_actual_qos() const
Definition service.hpp:225
::rclcpp::ServiceHandle< S > UniquePtr
rclcpp::Service<S>::UniquePtr — see SharedPtr.
Definition service.hpp:164
bool is_valid() const
Check if the service was registered.
Definition service.hpp:183
Service()
Definition service.hpp:279
typename S::Response ResponseType
Definition service.hpp:167
::rclcpp::ServiceHandle< S > SharedPtr
Definition service.hpp:158
::rclcpp::ServiceHandle< S > ConstSharedPtr
Definition service.hpp:162
Inline storage-size macros for opaque entity buffers.
int nros_cpp_ret_t
Definition future.hpp:25
constexpr nros_cpp_qos_t qos_to_ffi(const QoS &qos)
Definition qos.hpp:719
Definition rclcpp.hpp:75
constexpr bool operator==(const QoS &left, const QoS &right)
Definition qos.hpp:518
constexpr bool operator!=(const QoS &left, const QoS &right)
Definition qos.hpp:532
@ NotInitialized
Definition result.hpp:131
ResultOf< void > Result
Definition result.hpp:247
rclcpp::Node and global session helpers.
rclcpp::QoS — full DDS-shaped QoS settings (Phase 108.B.7).
**auto handle
Definition client.hpp:127
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
bool(* nros_cpp_service_request_id_callback_t)(const uint8_t *req, size_t req_len, rmw_request_id_t *request_id, uint8_t *resp, size_t resp_cap, size_t *resp_len, void *ctx)
Issue 2081 — a service trampoline that also receives the request's id.
Definition service.hpp:71
nros_cpp_ret_t nros_cpp_service_server_register_with_request_id(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_id_callback_t callback, void *context, const uint8_t *capture, size_t capture_len, void(*capture_drop)(void *), uint8_t sched_context, size_t *out_handle_id)
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)
nros_cpp_ret_t nros_cpp_service_server_register_capturing(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, const uint8_t *capture, size_t capture_len, void(*capture_drop)(void *), uint8_t sched_context, size_t *out_handle_id)
Definition qos.hpp:84