From 424102b56adc64149f3b0d941171142ab6a1aa22 Mon Sep 17 00:00:00 2001 From: Filippo Brizzi Date: Wed, 24 Jan 2024 10:20:15 +0000 Subject: [PATCH] add callback registration for subscribers listener --- examples/z_pub.c | 18 +++- include/zenoh_commons.h | 65 +++++++++++ include/zenoh_macros.h | 138 +++++++++++++----------- src/closures/matching_status_closure.rs | 86 +++++++++++++++ src/closures/mod.rs | 3 + src/publisher.rs | 90 ++++++++++++++-- 6 files changed, 324 insertions(+), 76 deletions(-) create mode 100644 src/closures/matching_status_closure.rs diff --git a/examples/z_pub.c b/examples/z_pub.c index 04a405ccd..0e3f170d7 100644 --- a/examples/z_pub.c +++ b/examples/z_pub.c @@ -22,15 +22,25 @@ #include #endif +void matching_status_handler(const z_matching_status_t *matching_status, void *arg) { + if (matching_status->matching) { + printf("Subscriber matched\n"); + } else { + printf("No Subscribers matched\n"); + } +} + int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-c-pub"; char *value = "Pub from C!"; + bool add_matching_listener = false; if (argc > 1) keyexpr = argv[1]; if (argc > 2) value = argv[2]; + if (argc > 3) add_matching_listener = atoi(argv[3]); z_owned_config_t config = z_config_default(); - if (argc > 3) { + if (argc > 4) { if (zc_config_insert_json(z_loan(config), Z_CONFIG_CONNECT_KEY, argv[3]) < 0) { printf( "Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a " @@ -54,6 +64,12 @@ int main(int argc, char **argv) { exit(-1); } + z_owned_matching_listener_t listener; + if (add_matching_listener) { + z_owned_closure_matching_status_t callback = z_closure(matching_status_handler); + listener = z_publisher_matching_listener_callback(z_loan(pub), z_move(callback)); + } + char buf[256]; for (int idx = 0; 1; ++idx) { sleep(1); diff --git a/include/zenoh_commons.h b/include/zenoh_commons.h index 34126a112..a848cf3f2 100644 --- a/include/zenoh_commons.h +++ b/include/zenoh_commons.h @@ -259,6 +259,36 @@ typedef struct z_owned_closure_hello_t { void (*call)(struct z_owned_hello_t*, void*); void (*drop)(void*); } z_owned_closure_hello_t; +/** + * A struct that indicates if there exist Subscribers matching the Publisher's key expression. + * + * Members: + * bool matching: true if there exist Subscribers matching the Publisher's key expression. + */ +typedef struct z_matching_status_t { + bool matching; +} z_matching_status_t; +/** + * A closure is a structure that contains all the elements for stateful, memory-leak-free callbacks: + * + * Members: + * void *context: a pointer to an arbitrary state. + * void *call(const struct z_owned_reply_t*, const void *context): the typical callback function. `context` will be passed as its last argument. + * void *drop(void*): allows the callback's state to be freed. + * + * Closures are not guaranteed not to be called concurrently. + * + * It is guaranteed that: + * + * - `call` will never be called once `drop` has started. + * - `drop` will only be called **once**, and **after every** `call` has ended. + * - The two previous guarantees imply that `call` and `drop` are never called concurrently. + */ +typedef struct z_owned_closure_matching_status_t { + void *context; + void (*call)(const struct z_matching_status_t*, void*); + void (*drop)(void*); +} z_owned_closure_matching_status_t; /** * Owned variant of a Query received by a Queryable. * @@ -686,6 +716,21 @@ typedef struct z_publisher_t { typedef struct z_publisher_delete_options_t { uint8_t __dummy; } z_publisher_delete_options_t; +/** + * An owned zenoh matching listener. Destroying the matching listener cancels the subscription. + * + * Like most `z_owned_X_t` types, you may obtain an instance of `z_X_t` by loaning it using `z_X_loan(&val)`. + * The `z_loan(val)` macro, available if your compiler supports C11's `_Generic`, is equivalent to writing `z_X_loan(&val)`. + * + * Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. + * To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. + * After a move, `val` will still exist, but will no longer be valid. The destructors are double-drop-safe, but other functions will still trust that your `val` is valid. + * + * To check if `val` is still valid, you may use `z_X_check(&val)` or `z_check(val)` if your compiler supports `_Generic`, which will return `true` if `val` is valid. + */ +typedef struct ALIGN(8) z_owned_matching_listener_t { + uint64_t _0[4]; +} z_owned_matching_listener_t; /** * Options passed to the :c:func:`z_publisher_put` function. * @@ -1077,6 +1122,20 @@ ZENOHC_API struct z_owned_closure_hello_t z_closure_hello_null(void); * Calls the closure. Calling an uninitialized closure is a no-op. */ ZENOHC_API +void z_closure_matching_status_call(const struct z_owned_closure_matching_status_t *closure, + const struct z_matching_status_t *sample); +/** + * Drops the closure. Droping an uninitialized closure is a no-op. + */ +ZENOHC_API void z_closure_matching_status_drop(struct z_owned_closure_matching_status_t *closure); +/** + * Constructs a null safe-to-drop value of 'z_owned_closure_matching_status_t' type + */ +ZENOHC_API struct z_owned_closure_matching_status_t z_closure_matching_status_null(void); +/** + * Calls the closure. Calling an uninitialized closure is a no-op. + */ +ZENOHC_API void z_closure_owned_query_call(const struct z_owned_closure_owned_query_t *closure, struct z_owned_query_t *query); /** @@ -1593,6 +1652,12 @@ ZENOHC_API struct z_owned_keyexpr_t z_publisher_keyexpr(struct z_publisher_t pub * Returns a :c:type:`z_publisher_t` loaned from `p`. */ ZENOHC_API struct z_publisher_t z_publisher_loan(const struct z_owned_publisher_t *p); +/** + * Register callback for notifying subscribers matching. + */ +ZENOHC_API +struct z_owned_matching_listener_t z_publisher_matching_listener_callback(struct z_publisher_t publisher, + struct z_owned_closure_matching_status_t *callback); /** * Constructs a null safe-to-drop value of 'z_owned_publisher_t' type */ diff --git a/include/zenoh_macros.h b/include/zenoh_macros.h index 5c93264b8..d9d5d07d9 100644 --- a/include/zenoh_macros.h +++ b/include/zenoh_macros.h @@ -18,65 +18,67 @@ )(&x) #define z_drop(x) \ - _Generic((x), z_owned_session_t * : z_close, \ - z_owned_publisher_t * : z_undeclare_publisher, \ - z_owned_keyexpr_t * : z_keyexpr_drop, \ - z_owned_config_t * : z_config_drop, \ - z_owned_scouting_config_t * : z_scouting_config_drop, \ - z_owned_pull_subscriber_t * : z_undeclare_pull_subscriber, \ - z_owned_subscriber_t * : z_undeclare_subscriber, \ - z_owned_queryable_t * : z_undeclare_queryable, \ - z_owned_encoding_t * : z_encoding_drop, \ - z_owned_reply_t * : z_reply_drop, \ - z_owned_hello_t * : z_hello_drop, \ - z_owned_str_t * : z_str_drop, \ - z_owned_query_t * : z_query_drop, \ - z_owned_closure_sample_t * : z_closure_sample_drop, \ - z_owned_closure_query_t * : z_closure_query_drop, \ - z_owned_closure_reply_t * : z_closure_reply_drop, \ - z_owned_closure_hello_t * : z_closure_hello_drop, \ - z_owned_closure_zid_t * : z_closure_zid_drop, \ - z_owned_reply_channel_closure_t * : z_reply_channel_closure_drop, \ - z_owned_query_channel_closure_t * : z_query_channel_closure_drop, \ - z_owned_reply_channel_t * : z_reply_channel_drop, \ - z_owned_query_channel_t * : z_query_channel_drop, \ - z_owned_bytes_map_t * : z_bytes_map_drop, \ - zc_owned_payload_t * : zc_payload_drop, \ - zc_owned_shmbuf_t * : zc_shmbuf_drop, \ - zc_owned_shm_manager_t * : zc_shm_manager_drop, \ - zc_owned_liveliness_token_t * : zc_liveliness_undeclare_token, \ - ze_owned_publication_cache_t * : ze_undeclare_publication_cache, \ - ze_owned_querying_subscriber_t * : ze_undeclare_querying_subscriber \ + _Generic((x), z_owned_session_t * : z_close, \ + z_owned_publisher_t * : z_undeclare_publisher, \ + z_owned_keyexpr_t * : z_keyexpr_drop, \ + z_owned_config_t * : z_config_drop, \ + z_owned_scouting_config_t * : z_scouting_config_drop, \ + z_owned_pull_subscriber_t * : z_undeclare_pull_subscriber, \ + z_owned_subscriber_t * : z_undeclare_subscriber, \ + z_owned_queryable_t * : z_undeclare_queryable, \ + z_owned_encoding_t * : z_encoding_drop, \ + z_owned_reply_t * : z_reply_drop, \ + z_owned_hello_t * : z_hello_drop, \ + z_owned_str_t * : z_str_drop, \ + z_owned_query_t * : z_query_drop, \ + z_owned_closure_sample_t * : z_closure_sample_drop, \ + z_owned_closure_query_t * : z_closure_query_drop, \ + z_owned_closure_reply_t * : z_closure_reply_drop, \ + z_owned_closure_hello_t * : z_closure_hello_drop, \ + z_owned_closure_zid_t * : z_closure_zid_drop, \ + z_owned_closure_matching_status_t * : z_closure_matching_status_drop, \ + z_owned_reply_channel_closure_t * : z_reply_channel_closure_drop, \ + z_owned_query_channel_closure_t * : z_query_channel_closure_drop, \ + z_owned_reply_channel_t * : z_reply_channel_drop, \ + z_owned_query_channel_t * : z_query_channel_drop, \ + z_owned_bytes_map_t * : z_bytes_map_drop, \ + zc_owned_payload_t * : zc_payload_drop, \ + zc_owned_shmbuf_t * : zc_shmbuf_drop, \ + zc_owned_shm_manager_t * : zc_shm_manager_drop, \ + zc_owned_liveliness_token_t * : zc_liveliness_undeclare_token, \ + ze_owned_publication_cache_t * : ze_undeclare_publication_cache, \ + ze_owned_querying_subscriber_t * : ze_undeclare_querying_subscriber \ )(x) #define z_null(x) (*x = \ - _Generic((x), z_owned_session_t * : z_session_null, \ - z_owned_publisher_t * : z_publisher_null, \ - z_owned_keyexpr_t * : z_keyexpr_null, \ - z_owned_config_t * : z_config_null, \ - z_owned_scouting_config_t * : z_scouting_config_null, \ - z_owned_pull_subscriber_t * : z_pull_subscriber_null, \ - z_owned_subscriber_t * : z_subscriber_null, \ - z_owned_queryable_t * : z_queryable_null, \ - z_owned_encoding_t * : z_encoding_null, \ - z_owned_reply_t * : z_reply_null, \ - z_owned_hello_t * : z_hello_null, \ - z_owned_str_t * : z_str_null, \ - z_owned_query_t * : z_query_null, \ - z_owned_closure_sample_t * : z_closure_sample_null, \ - z_owned_closure_query_t * : z_closure_query_null, \ - z_owned_closure_reply_t * : z_closure_reply_null, \ - z_owned_closure_hello_t * : z_closure_hello_null, \ - z_owned_closure_zid_t * : z_closure_zid_null, \ - z_owned_reply_channel_closure_t * : z_reply_channel_closure_null, \ - z_owned_reply_channel_t * : z_reply_channel_null, \ - z_owned_bytes_map_t * : z_bytes_map_null, \ - z_attachment_t * : z_attachment_null, \ - zc_owned_payload_t * : zc_payload_null, \ - zc_owned_shmbuf_t * : zc_shmbuf_null, \ - zc_owned_shm_manager_t * : zc_shm_manager_null, \ - ze_owned_publication_cache_t * : ze_publication_cache_null, \ - zc_owned_liveliness_token_t * : zc_liveliness_token_null \ + _Generic((x), z_owned_session_t * : z_session_null, \ + z_owned_publisher_t * : z_publisher_null, \ + z_owned_keyexpr_t * : z_keyexpr_null, \ + z_owned_config_t * : z_config_null, \ + z_owned_scouting_config_t * : z_scouting_config_null, \ + z_owned_pull_subscriber_t * : z_pull_subscriber_null, \ + z_owned_subscriber_t * : z_subscriber_null, \ + z_owned_queryable_t * : z_queryable_null, \ + z_owned_encoding_t * : z_encoding_null, \ + z_owned_reply_t * : z_reply_null, \ + z_owned_hello_t * : z_hello_null, \ + z_owned_str_t * : z_str_null, \ + z_owned_query_t * : z_query_null, \ + z_owned_closure_sample_t * : z_closure_sample_null, \ + z_owned_closure_query_t * : z_closure_query_null, \ + z_owned_closure_reply_t * : z_closure_reply_null, \ + z_owned_closure_hello_t * : z_closure_hello_null, \ + z_owned_closure_zid_t * : z_closure_zid_null, \ + z_owned_closure_matching_status_t * : z_closure_matching_status_null, \ + z_owned_reply_channel_closure_t * : z_reply_channel_closure_null, \ + z_owned_reply_channel_t * : z_reply_channel_null, \ + z_owned_bytes_map_t * : z_bytes_map_null, \ + z_attachment_t * : z_attachment_null, \ + zc_owned_payload_t * : zc_payload_null, \ + zc_owned_shmbuf_t * : zc_shmbuf_null, \ + zc_owned_shm_manager_t * : zc_shm_manager_null, \ + ze_owned_publication_cache_t * : ze_publication_cache_null, \ + zc_owned_liveliness_token_t * : zc_liveliness_token_null \ )()) #define z_check(x) \ @@ -106,14 +108,15 @@ )(&x) #define z_call(x, ...) \ - _Generic((x), z_owned_closure_sample_t : z_closure_sample_call, \ - z_owned_closure_query_t : z_closure_query_call, \ - z_owned_closure_owned_query_t : z_closure_owned_query_call, \ - z_owned_closure_reply_t : z_closure_reply_call, \ - z_owned_closure_hello_t : z_closure_hello_call, \ - z_owned_closure_zid_t : z_closure_zid_call, \ - z_owned_reply_channel_closure_t : z_reply_channel_closure_call,\ - z_owned_query_channel_closure_t : z_query_channel_closure_call \ + _Generic((x), z_owned_closure_sample_t : z_closure_sample_call, \ + z_owned_closure_query_t : z_closure_query_call, \ + z_owned_closure_owned_query_t : z_closure_owned_query_call, \ + z_owned_closure_reply_t : z_closure_reply_call, \ + z_owned_closure_hello_t : z_closure_hello_call, \ + z_owned_closure_zid_t : z_closure_zid_call, \ + z_owned_closure_matching_status_t : z_closure_matching_status_call, \ + z_owned_reply_channel_closure_t : z_reply_channel_closure_call, \ + z_owned_query_channel_closure_t : z_query_channel_closure_call \ ) (&x, __VA_ARGS__) // clang-format on @@ -176,6 +179,7 @@ template<> struct zenoh_drop_type { typedef void type; template<> struct zenoh_drop_type { typedef void type; }; template<> struct zenoh_drop_type { typedef void type; }; template<> struct zenoh_drop_type { typedef void type; }; +template<> struct zenoh_drop_type { typedef void type; }; template<> struct zenoh_drop_type { typedef void type; }; template<> struct zenoh_drop_type { typedef void type; }; template<> struct zenoh_drop_type { typedef void type; }; @@ -204,6 +208,7 @@ template<> inline void z_drop(z_owned_closure_query_t* v) { z_closure_query_drop template<> inline void z_drop(z_owned_closure_reply_t* v) { z_closure_reply_drop(v); } template<> inline void z_drop(z_owned_closure_hello_t* v) { z_closure_hello_drop(v); } template<> inline void z_drop(z_owned_closure_zid_t* v) { z_closure_zid_drop(v); } +template<> inline void z_drop(z_owned_closure_matching_status_t* v) { z_closure_matching_drop(v); } template<> inline void z_drop(z_owned_reply_channel_closure_t* v) { z_reply_channel_closure_drop(v); } template<> inline void z_drop(z_owned_reply_channel_t* v) { z_reply_channel_drop(v); } template<> inline void z_drop(z_owned_bytes_map_t* v) { z_bytes_map_drop(v); } @@ -232,6 +237,7 @@ inline void z_null(z_owned_closure_query_t& v) { v = z_closure_query_null(); } inline void z_null(z_owned_closure_reply_t& v) { v = z_closure_reply_null(); } inline void z_null(z_owned_closure_hello_t& v) { v = z_closure_hello_null(); } inline void z_null(z_owned_closure_zid_t& v) { v = z_closure_zid_null(); } +inline void z_null(z_owned_closure_matching_status_t& v) { v = z_closure_matching_status_null(); } inline void z_null(z_owned_reply_channel_closure_t& v) { v = z_reply_channel_closure_null(); } inline void z_null(z_owned_reply_channel_t& v) { v = z_reply_channel_null(); } inline void z_null(z_owned_bytes_map_t& v) { v = z_bytes_map_null(); } @@ -263,7 +269,7 @@ inline bool z_check(const zc_owned_liveliness_token_t& v) { return zc_liveliness inline bool z_check(const ze_owned_publication_cache_t& v) { return ze_publication_cache_check(&v); } inline bool z_check(const ze_owned_querying_subscriber_t& v) { return ze_querying_subscriber_check(&v); } -inline void z_call(const struct z_owned_closure_sample_t &closure, const struct z_sample_t *sample) +inline void z_call(const struct z_owned_closure_sample_t &closure, const struct z_sample_t *sample) { z_closure_sample_call(&closure, sample); } inline void z_call(const struct z_owned_closure_query_t &closure, const struct z_query_t *query) { z_closure_query_call(&closure, query); } @@ -273,6 +279,8 @@ inline void z_call(const struct z_owned_closure_hello_t &closure, struct z_owned { z_closure_hello_call(&closure, hello); } inline void z_call(const struct z_owned_closure_zid_t &closure, const struct z_id_t *zid) { z_closure_zid_call(&closure, zid); } +inline void z_call(const struct z_owned_closure_matching_status_t &closure, const struct z_matching_status_t *matching_status) + { z_closure_matching_status_call(&closure, matching_status); } inline bool z_call(const struct z_owned_reply_channel_closure_t &closure, struct z_owned_reply_t *sample) { return z_reply_channel_closure_call(&closure, sample); } // clang-format on diff --git a/src/closures/matching_status_closure.rs b/src/closures/matching_status_closure.rs new file mode 100644 index 000000000..ecce757dd --- /dev/null +++ b/src/closures/matching_status_closure.rs @@ -0,0 +1,86 @@ +use libc::c_void; + +use crate::z_matching_status_t; +/// A closure is a structure that contains all the elements for stateful, memory-leak-free callbacks: +/// +/// Members: +/// void *context: a pointer to an arbitrary state. +/// void *call(const struct z_owned_reply_t*, const void *context): the typical callback function. `context` will be passed as its last argument. +/// void *drop(void*): allows the callback's state to be freed. +/// +/// Closures are not guaranteed not to be called concurrently. +/// +/// It is guaranteed that: +/// +/// - `call` will never be called once `drop` has started. +/// - `drop` will only be called **once**, and **after every** `call` has ended. +/// - The two previous guarantees imply that `call` and `drop` are never called concurrently. +#[repr(C)] +pub struct z_owned_closure_matching_status_t { + context: *mut c_void, + call: Option, + drop: Option, +} + +impl z_owned_closure_matching_status_t { + pub fn empty() -> Self { + z_owned_closure_matching_status_t { + context: std::ptr::null_mut(), + call: None, + drop: None, + } + } +} +unsafe impl Send for z_owned_closure_matching_status_t {} +unsafe impl Sync for z_owned_closure_matching_status_t {} +impl Drop for z_owned_closure_matching_status_t { + fn drop(&mut self) { + if let Some(drop) = self.drop { + drop(self.context) + } + } +} +/// Constructs a null safe-to-drop value of 'z_owned_closure_matching_status_t' type +#[no_mangle] +pub extern "C" fn z_closure_matching_status_null() -> z_owned_closure_matching_status_t { + z_owned_closure_matching_status_t::empty() +} +/// Calls the closure. Calling an uninitialized closure is a no-op. +#[no_mangle] +pub extern "C" fn z_closure_matching_status_call( + closure: &z_owned_closure_matching_status_t, + sample: &z_matching_status_t, +) { + match closure.call { + Some(call) => call(sample, closure.context), + None => { + log::error!("Attempted to call an uninitialized closure!"); + } + } +} +/// Drops the closure. Droping an uninitialized closure is a no-op. +#[no_mangle] +pub extern "C" fn z_closure_matching_status_drop(closure: &mut z_owned_closure_matching_status_t) { + let mut empty_closure = z_owned_closure_matching_status_t::empty(); + std::mem::swap(&mut empty_closure, closure); +} +impl From for z_owned_closure_matching_status_t { + fn from(f: F) -> Self { + let this = Box::into_raw(Box::new(f)) as _; + extern "C" fn call( + response: &z_matching_status_t, + this: *mut c_void, + ) { + let this = unsafe { &*(this as *const F) }; + this(response) + } + extern "C" fn drop(this: *mut c_void) { + std::mem::drop(unsafe { Box::from_raw(this as *mut F) }) + } + z_owned_closure_matching_status_t { + context: this, + call: Some(call::), + drop: Some(drop::), + } + } +} diff --git a/src/closures/mod.rs b/src/closures/mod.rs index 072f8a66e..74b2748aa 100644 --- a/src/closures/mod.rs +++ b/src/closures/mod.rs @@ -31,3 +31,6 @@ mod query_channel; pub use hello_closure::*; mod hello_closure; + +pub use matching_status_closure::*; +mod matching_status_closure; diff --git a/src/publisher.rs b/src/publisher.rs index 63a06a535..e1bc45fa7 100644 --- a/src/publisher.rs +++ b/src/publisher.rs @@ -12,11 +12,13 @@ // ZettaScale Zenoh team, // +use crate::z_closure_matching_status_call; +use crate::z_owned_closure_matching_status_t; use std::ops::{Deref, DerefMut}; - use zenoh::prelude::SessionDeclarations; use zenoh::{ prelude::{Priority, Value}, + publication::MatchingListener, publication::Publisher, sample::AttachmentBuilder, }; @@ -57,12 +59,12 @@ pub extern "C" fn z_publisher_options_default() -> z_publisher_options_t { /// An owned zenoh publisher. /// -/// Like most `z_owned_X_t` types, you may obtain an instance of `z_X_t` by loaning it using `z_X_loan(&val)`. -/// The `z_loan(val)` macro, available if your compiler supports C11's `_Generic`, is equivalent to writing `z_X_loan(&val)`. +/// Like most `z_owned_X_t` types, you may obtain an instance of `z_X_t` by loaning it using `z_X_loan(&val)`. +/// The `z_loan(val)` macro, available if your compiler supports C11's `_Generic`, is equivalent to writing `z_X_loan(&val)`. /// -/// Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. -/// To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. -/// After a move, `val` will still exist, but will no longer be valid. The destructors are double-drop-safe, but other functions will still trust that your `val` is valid. +/// Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. +/// To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. +/// After a move, `val` will still exist, but will no longer be valid. The destructors are double-drop-safe, but other functions will still trust that your `val` is valid. /// /// To check if `val` is still valid, you may use `z_X_check(&val)` or `z_check(val)` if your compiler supports `_Generic`, which will return `true` if `val` is valid. #[cfg(not(target_arch = "arm"))] @@ -113,8 +115,8 @@ impl z_owned_publisher_t { /// To check if the publisher decalration succeeded and if the publisher is still valid, /// you may use `z_publisher_check(&val)` or `z_check(val)` if your compiler supports `_Generic`, which will return `true` if `val` is valid. /// -/// Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. -/// To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. +/// Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. +/// To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. /// After a move, `val` will still exist, but will no longer be valid. The destructors are double-drop-safe, but other functions will still trust that your `val` is valid. /// /// Example: @@ -125,7 +127,7 @@ impl z_owned_publisher_t { /// z_owned_publisher_t pub = z_declare_publisher(z_loan(s), z_keyexpr(expr), NULL); /// /// is equivalent to initializing and passing the default publisher options: -/// +/// /// .. code-block:: C /// /// z_publisher_options_t opts = z_publisher_options_default(); @@ -339,7 +341,6 @@ pub struct z_publisher_delete_options_t { pub extern "C" fn z_publisher_delete_options_default() -> z_publisher_delete_options_t { z_publisher_delete_options_t { __dummy: 0 } } - /// Sends a `DELETE` message onto the publisher's key expression. /// /// Returns: @@ -373,6 +374,75 @@ pub extern "C" fn z_publisher_keyexpr(publisher: z_publisher_t) -> z_owned_keyex } } +/// An owned zenoh matching listener. Destroying the matching listener cancels the subscription. +/// +/// Like most `z_owned_X_t` types, you may obtain an instance of `z_X_t` by loaning it using `z_X_loan(&val)`. +/// The `z_loan(val)` macro, available if your compiler supports C11's `_Generic`, is equivalent to writing `z_X_loan(&val)`. +/// +/// Like all `z_owned_X_t`, an instance will be destroyed by any function which takes a mutable pointer to said instance, as this implies the instance's inners were moved. +/// To make this fact more obvious when reading your code, consider using `z_move(val)` instead of `&val` as the argument. +/// After a move, `val` will still exist, but will no longer be valid. The destructors are double-drop-safe, but other functions will still trust that your `val` is valid. +/// +/// To check if `val` is still valid, you may use `z_X_check(&val)` or `z_check(val)` if your compiler supports `_Generic`, which will return `true` if `val` is valid. +#[repr(C, align(8))] +pub struct z_owned_matching_listener_t([u64; 4]); + +impl_guarded_transmute!( + Option>, + z_owned_matching_listener_t +); + +impl From>> for z_owned_matching_listener_t { + fn from(val: Option>) -> Self { + val.transmute() + } +} + +impl z_owned_matching_listener_t { + pub fn null() -> Self { + None.into() + } +} + +/// A struct that indicates if there exist Subscribers matching the Publisher's key expression. +/// +/// Members: +/// bool matching: true if there exist Subscribers matching the Publisher's key expression. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct z_matching_status_t { + pub matching: bool, +} + +/// Register callback for notifying subscribers matching. +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +pub extern "C" fn z_publisher_matching_listener_callback( + publisher: z_publisher_t, + callback: &mut z_owned_closure_matching_status_t, +) -> z_owned_matching_listener_t { + let mut closure = z_owned_closure_matching_status_t::empty(); + std::mem::swap(callback, &mut closure); + { + if let Some(p) = publisher.as_ref() { + let listener = p + .matching_listener() + .callback_mut(move |matching_status| { + let status = z_matching_status_t { + matching: matching_status.matching_subscribers(), + }; + z_closure_matching_status_call(&closure, &status); + }) + .res() + .unwrap(); + Some(listener) + } else { + None + } + } + .into() +} + /// Undeclares the given :c:type:`z_owned_publisher_t`, droping it and invalidating it for double-drop safety. #[no_mangle] #[allow(clippy::missing_safety_doc)]