diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4bad92489..33098a4cf 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -121,7 +121,7 @@ jobs: static-code-analysis-cpp: needs: preflight-check if: ${{ needs.changes.outputs.source-code == 'true' }} - timeout-minutes: 10 + timeout-minutes: 20 runs-on: ubuntu-latest steps: - name: Checkout sources diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index 86f758753..1488fb322 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -11,6 +11,8 @@ conflicts when merging. --> +* Full C/C++ Language bindings for all features + [#264](https://github.com/eclipse-iceoryx/iceoryx2/issues/264) * Read LogLevel from environment variable [#396](https://github.com/eclipse-iceoryx/iceoryx2/issues/396) * Lookup config file in default locations diff --git a/iceoryx2-ffi/cxx/CMakeLists.txt b/iceoryx2-ffi/cxx/CMakeLists.txt index 34b96e72c..dd3c6af20 100644 --- a/iceoryx2-ffi/cxx/CMakeLists.txt +++ b/iceoryx2-ffi/cxx/CMakeLists.txt @@ -41,6 +41,8 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/attribute_specifier.cpp src/attribute_verifier.cpp src/config.cpp + src/dynamic_config_event.cpp + src/dynamic_config_publish_subscribe.cpp src/event_id.cpp src/file_descriptor.cpp src/header_publish_subscribe.cpp @@ -59,6 +61,7 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/port_factory_notifier.cpp src/service.cpp src/service_builder_event.cpp + src/service_id.cpp src/service_name.cpp src/static_config.cpp src/static_config_event.cpp diff --git a/iceoryx2-ffi/cxx/include/iox2/attribute_set.hpp b/iceoryx2-ffi/cxx/include/iox2/attribute_set.hpp index 8ee50714f..14df21363 100644 --- a/iceoryx2-ffi/cxx/include/iox2/attribute_set.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/attribute_set.hpp @@ -20,6 +20,8 @@ #include namespace iox2 { +class AttributeSet; + /// Represents all service attributes. They can be set when the service is created. /// /// @attention The parent from which the view was extracted MUST live longer than the @@ -49,6 +51,9 @@ class AttributeSetView { void get_key_values(const Attribute::Key& key, const iox::function& callback) const; + /// Creates a copy of the [`AttributeSetView`] that owns the attributes. + auto to_owned() const -> AttributeSet; + private: template friend class PortFactoryPublishSubscribe; @@ -56,13 +61,59 @@ class AttributeSetView { friend class PortFactoryEvent; friend class AttributeVerifier; friend class AttributeSpecifier; + friend class StaticConfig; + friend class AttributeSet; - explicit AttributeSetView(iox2_attribute_set_h_ref handle); + explicit AttributeSetView(iox2_attribute_set_ptr handle); - iox2_attribute_set_h_ref m_handle = nullptr; + iox2_attribute_set_ptr m_handle = nullptr; }; + +/// Represents all service attributes. They can be set when the service is created. +class AttributeSet { + public: + AttributeSet(const AttributeSet&) = delete; + AttributeSet(AttributeSet&& rhs) noexcept; + auto operator=(const AttributeSet&) -> AttributeSet& = delete; + auto operator=(AttributeSet&& rhs) noexcept -> AttributeSet&; + ~AttributeSet(); + + /// Returns the number of [`Attribute`]s stored inside the [`AttributeSet`]. + auto len() const -> uint64_t; + + /// Returns a [`AttributeView`] at a specific index. The number of indices is returned via + /// [`AttributeSet::len()`]. + auto at(uint64_t index) const -> AttributeView; + + /// Returns the number of values stored under a specific key. If the key does not exist it + /// returns 0. + auto get_key_value_len(const Attribute::Key& key) const -> uint64_t; + + /// Returns a value of a key at a specific index. The index enumerates the values of the key + /// if the key has multiple values. The values are always stored at the same position during + /// the lifetime of the service but they can change when the process is recreated by another + /// process when the system restarts. + /// If the key does not exist or it does not have a value at the specified index, it returns + /// [`None`]. + auto get_key_value_at(const Attribute::Key& key, uint64_t idx) -> iox::optional; + + /// Returns all values to a specific key + void get_key_values(const Attribute::Key& key, + const iox::function& callback) const; + + private: + friend class AttributeSetView; + + explicit AttributeSet(iox2_attribute_set_h handle); + void drop(); + + iox2_attribute_set_h m_handle = nullptr; + AttributeSetView m_view; +}; + } // namespace iox2 auto operator<<(std::ostream& stream, const iox2::AttributeSetView& value) -> std::ostream&; +auto operator<<(std::ostream& stream, const iox2::AttributeSet& value) -> std::ostream&; #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/dynamic_config_event.hpp b/iceoryx2-ffi/cxx/include/iox2/dynamic_config_event.hpp index 92280aa1d..3d01c8b6d 100644 --- a/iceoryx2-ffi/cxx/include/iox2/dynamic_config_event.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/dynamic_config_event.hpp @@ -13,19 +13,32 @@ #ifndef IOX2_DYNAMIC_CONFIG_EVENT_HPP #define IOX2_DYNAMIC_CONFIG_EVENT_HPP -#include "iox/assertions_addendum.hpp" +#include "iox2/internal/iceoryx2.hpp" #include namespace iox2 { class DynamicConfigEvent { public: - auto number_of_listeners() const -> uint64_t { - IOX_TODO(); - } - auto number_of_notifiers() const -> uint64_t { - IOX_TODO(); - } + DynamicConfigEvent(const DynamicConfigEvent&) = delete; + DynamicConfigEvent(DynamicConfigEvent&&) = delete; + auto operator=(const DynamicConfigEvent&) -> DynamicConfigEvent& = delete; + auto operator=(DynamicConfigEvent&&) -> DynamicConfigEvent& = delete; + ~DynamicConfigEvent() = default; + + /// Returns how many [`Listener`] ports are currently connected. + auto number_of_listeners() const -> uint64_t; + + /// Returns how many [`Notifier`] ports are currently connected. + auto number_of_notifiers() const -> uint64_t; + + private: + template + friend class PortFactoryEvent; + + explicit DynamicConfigEvent(iox2_port_factory_event_h handle); + + iox2_port_factory_event_h m_handle = nullptr; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/dynamic_config_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/dynamic_config_publish_subscribe.hpp index f7b2e5013..29b85f58f 100644 --- a/iceoryx2-ffi/cxx/include/iox2/dynamic_config_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/dynamic_config_publish_subscribe.hpp @@ -13,19 +13,36 @@ #ifndef IOX2_DYNAMIC_CONFIG_PUBLISH_SUBSCRIBE_HPP #define IOX2_DYNAMIC_CONFIG_PUBLISH_SUBSCRIBE_HPP -#include "iox/assertions_addendum.hpp" +#include "iox2/internal/iceoryx2.hpp" #include namespace iox2 { + +/// The dynamic configuration of an +/// [`MessagingPattern::PublishSubscribe`] based service. Contains dynamic +/// parameters like the connected endpoints etc.. class DynamicConfigPublishSubscribe { public: - auto number_of_publishers() const -> uint64_t { - IOX_TODO(); - } - auto number_of_subscribers() const -> uint64_t { - IOX_TODO(); - } + DynamicConfigPublishSubscribe(const DynamicConfigPublishSubscribe&) = delete; + DynamicConfigPublishSubscribe(DynamicConfigPublishSubscribe&&) = delete; + auto operator=(const DynamicConfigPublishSubscribe&) -> DynamicConfigPublishSubscribe& = delete; + auto operator=(DynamicConfigPublishSubscribe&&) -> DynamicConfigPublishSubscribe& = delete; + ~DynamicConfigPublishSubscribe() = default; + + /// Returns how many [`Publisher`] ports are currently connected. + auto number_of_publishers() const -> uint64_t; + + /// Returns how many [`Subscriber`] ports are currently connected. + auto number_of_subscribers() const -> uint64_t; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit DynamicConfigPublishSubscribe(iox2_port_factory_pub_sub_h handle); + + iox2_port_factory_pub_sub_h m_handle = nullptr; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/internal/callback_context.hpp b/iceoryx2-ffi/cxx/include/iox2/internal/callback_context.hpp index 99bee02c2..af395f487 100644 --- a/iceoryx2-ffi/cxx/include/iox2/internal/callback_context.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/internal/callback_context.hpp @@ -13,7 +13,33 @@ #ifndef IOX2_INTERNAL_CALLBACK_CONTEXT_HPP #define IOX2_INTERNAL_CALLBACK_CONTEXT_HPP +#include "iox/optional.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/node_details.hpp" +#include "iox2/node_id.hpp" +#include "iox2/node_name.hpp" +#include "iox2/node_state.hpp" +#include "iox2/service_type.hpp" + namespace iox2::internal { + +/// Building block to provide a type-safe context pointer to a C callback +/// that has a `void*` context argument. +/// The context could be hereby a user provided clojure with capture or +/// any other C++ object. +/// +/// # Example +/// +/// ``` +/// void some_c_callback(void* context) { +/// auto ctx = internal::ctx_cast(context); +/// ctx->value(); // access underlying my_context_object +/// } +/// +/// SomeType my_context_object; +/// auto ctx = internal::ctx(my_context_object); +/// some_c_callback(static_cast(&ctx)); +/// ``` template class CallbackContext { public: @@ -30,14 +56,59 @@ class CallbackContext { }; template -auto ctx(const T& ptr) -> CallbackContext { +inline auto ctx(const T& ptr) -> CallbackContext { return CallbackContext(ptr); } template -auto ctx_cast(void* ptr) -> CallbackContext* { +inline auto ctx_cast(void* ptr) -> CallbackContext* { return static_cast*>(ptr); } + +template +// NOLINTBEGIN(readability-function-size) +auto list_callback(iox2_node_state_e node_state, + iox2_node_id_ptr node_id_ptr, + const char* executable, + iox2_node_name_ptr node_name, + iox2_config_ptr config, + iox2_callback_context context) -> iox2_callback_progression_e { + auto node_details = [&] { + if (node_id_ptr == nullptr || config == nullptr) { + return iox::optional(); + } + + return iox::optional(NodeDetails { + iox::FileName::create(iox::string(iox::TruncateToCapacity, executable)) + .expect("The executable file name is always valid."), + NodeNameView { node_name }.to_owned(), + Config {} }); + }(); + + iox2_node_id_h node_id_handle = nullptr; + iox2_node_id_clone_from_ptr(nullptr, node_id_ptr, &node_id_handle); + NodeId node_id { node_id_handle }; + + auto node_state_object = [&] { + switch (node_state) { + case iox2_node_state_e_ALIVE: + return NodeState { AliveNodeView { node_id, node_details } }; + case iox2_node_state_e_DEAD: + return NodeState { DeadNodeView { AliveNodeView { node_id, node_details } } }; + case iox2_node_state_e_UNDEFINED: + return NodeState { iox2_node_state_e_UNDEFINED, node_id }; + case iox2_node_state_e_INACCESSIBLE: + return NodeState { iox2_node_state_e_INACCESSIBLE, node_id }; + } + + IOX_UNREACHABLE(); + }(); + + auto* callback = internal::ctx_cast)>>(context); + return iox::into(callback->value()(node_state_object)); +} +// NOLINTEND(readability-function-size) + } // namespace iox2::internal #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/node_details.hpp b/iceoryx2-ffi/cxx/include/iox2/node_details.hpp index b0fb3c817..a6aa4e2a8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/node_details.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/node_details.hpp @@ -30,12 +30,12 @@ class NodeDetails { private: template - friend auto list_callback(iox2_node_state_e, - iox2_node_id_ptr, - const char* executable, - iox2_node_name_ptr, - iox2_config_ptr, - iox2_callback_context) -> iox2_callback_progression_e; + friend auto internal::list_callback(iox2_node_state_e, + iox2_node_id_ptr, + const char* executable, + iox2_node_name_ptr, + iox2_config_ptr, + iox2_callback_context) -> iox2_callback_progression_e; NodeDetails(iox::FileName executable, NodeName name, Config config); diff --git a/iceoryx2-ffi/cxx/include/iox2/node_id.hpp b/iceoryx2-ffi/cxx/include/iox2/node_id.hpp index 26f5e7271..2a647b171 100644 --- a/iceoryx2-ffi/cxx/include/iox2/node_id.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/node_id.hpp @@ -21,6 +21,13 @@ #include namespace iox2 { +namespace internal { +template +auto list_callback( + iox2_node_state_e, iox2_node_id_ptr, const char*, iox2_node_name_ptr, iox2_config_ptr, iox2_callback_context) + -> iox2_callback_progression_e; +} + /// The system-wide unique id of a [`Node`] class NodeId { public: @@ -48,11 +55,10 @@ class NodeId { template friend class DeadNodeView; template - friend auto list_callback( + friend auto internal::list_callback( iox2_node_state_e, iox2_node_id_ptr, const char*, iox2_node_name_ptr, iox2_config_ptr, iox2_callback_context) -> iox2_callback_progression_e; - explicit NodeId(iox2_node_id_h handle); void drop(); diff --git a/iceoryx2-ffi/cxx/include/iox2/node_name.hpp b/iceoryx2-ffi/cxx/include/iox2/node_name.hpp index f92f436e1..c8480d94a 100644 --- a/iceoryx2-ffi/cxx/include/iox2/node_name.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/node_name.hpp @@ -19,6 +19,15 @@ #include "iox2/semantic_string.hpp" namespace iox2 { +namespace internal { +template +auto list_callback(iox2_node_state_e, + iox2_node_id_ptr, + const char* executable, + iox2_node_name_ptr, + iox2_config_ptr, + iox2_callback_context) -> iox2_callback_progression_e; +} class NodeName; /// Non-owning view of a [`NodeName`]. @@ -41,12 +50,12 @@ class NodeNameView { friend class Node; friend class NodeName; template - friend auto list_callback(iox2_node_state_e, - iox2_node_id_ptr, - const char* executable, - iox2_node_name_ptr, - iox2_config_ptr, - iox2_callback_context) -> iox2_callback_progression_e; + friend auto internal::list_callback(iox2_node_state_e, + iox2_node_id_ptr, + const char* executable, + iox2_node_name_ptr, + iox2_config_ptr, + iox2_callback_context) -> iox2_callback_progression_e; explicit NodeNameView(iox2_node_name_ptr ptr); iox2_node_name_ptr m_ptr = nullptr; diff --git a/iceoryx2-ffi/cxx/include/iox2/node_state.hpp b/iceoryx2-ffi/cxx/include/iox2/node_state.hpp index 672876130..401be4d12 100644 --- a/iceoryx2-ffi/cxx/include/iox2/node_state.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/node_state.hpp @@ -102,7 +102,7 @@ class NodeState { private: template - friend auto list_callback( + friend auto internal::list_callback( iox2_node_state_e, iox2_node_id_ptr, const char*, iox2_node_name_ptr, iox2_config_ptr, iox2_callback_context) -> iox2_callback_progression_e; diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_event.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_event.hpp index 56f24987f..b2fef8d1f 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_event.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_event.hpp @@ -44,7 +44,7 @@ class PortFactoryEvent { auto name() const -> ServiceNameView; /// Returns the [`ServiceId`] of the [`Service`] - auto service_id() const -> const ServiceId&; + auto service_id() const -> ServiceId; /// Returns the attributes defined in the [`Service`] auto attributes() const -> AttributeSetView; @@ -55,7 +55,7 @@ class PortFactoryEvent { /// Returns the DynamicConfig of the [`Service`]. /// Contains all dynamic settings, like the current participants etc.. - auto dynamic_config() const -> const DynamicConfigEvent&; + auto dynamic_config() const -> DynamicConfigEvent; /// Iterates over all [`Node`]s of the [`Service`] /// and calls for every [`Node`] the provided callback. If an error occurs diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp index 431c566de..f0d2d7cef 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp @@ -19,6 +19,7 @@ #include "iox2/attribute_set.hpp" #include "iox2/callback_progression.hpp" #include "iox2/dynamic_config_publish_subscribe.hpp" +#include "iox2/internal/callback_context.hpp" #include "iox2/internal/iceoryx2.hpp" #include "iox2/node_failure_enums.hpp" #include "iox2/node_state.hpp" @@ -44,10 +45,10 @@ class PortFactoryPublishSubscribe { auto operator=(const PortFactoryPublishSubscribe&) -> PortFactoryPublishSubscribe& = delete; /// Returns the [`ServiceName`] of the service - auto name() const -> const ServiceName&; + auto name() const -> ServiceNameView; /// Returns the [`ServiceId`] of the [`Service`] - auto service_id() const -> const ServiceId&; + auto service_id() const -> ServiceId; /// Returns the attributes defined in the [`Service`] auto attributes() const -> AttributeSetView; @@ -58,7 +59,7 @@ class PortFactoryPublishSubscribe { /// Returns the DynamicConfig of the [`Service`]. /// Contains all dynamic settings, like the current participants etc.. - auto dynamic_config() const -> const DynamicConfigPublishSubscribe&; + auto dynamic_config() const -> DynamicConfigPublishSubscribe; /// Iterates over all [`Node`]s of the [`Service`] /// and calls for every [`Node`] the provided callback. If an error occurs @@ -121,13 +122,17 @@ inline PortFactoryPublishSubscribe::~PortFactoryPublishS } template -inline auto PortFactoryPublishSubscribe::name() const -> const ServiceName& { - IOX_TODO(); +inline auto PortFactoryPublishSubscribe::name() const -> ServiceNameView { + const auto* service_name_ptr = iox2_port_factory_pub_sub_service_name(&m_handle); + return ServiceNameView(service_name_ptr); } template -inline auto PortFactoryPublishSubscribe::service_id() const -> const ServiceId& { - IOX_TODO(); +inline auto PortFactoryPublishSubscribe::service_id() const -> ServiceId { + iox::UninitializedArray buffer; + iox2_port_factory_pub_sub_service_id(&m_handle, &buffer[0], IOX2_SERVICE_ID_LENGTH); + + return ServiceId(iox::string(iox::TruncateToCapacity, &buffer[0])); } template @@ -145,15 +150,23 @@ inline auto PortFactoryPublishSubscribe::static_config() template inline auto PortFactoryPublishSubscribe::dynamic_config() const - -> const DynamicConfigPublishSubscribe& { - IOX_TODO(); + -> DynamicConfigPublishSubscribe { + return DynamicConfigPublishSubscribe(m_handle); } template inline auto PortFactoryPublishSubscribe::nodes( - [[maybe_unused]] const iox::function)>& callback) const - -> iox::expected { - IOX_TODO(); + const iox::function)>& callback) const -> iox::expected { + auto ctx = internal::ctx(callback); + + const auto ret_val = + iox2_port_factory_pub_sub_nodes(&m_handle, internal::list_callback, static_cast(&ctx)); + + if (ret_val == IOX2_OK) { + return iox::ok(); + } + + return iox::err(iox::into(ret_val)); } template diff --git a/iceoryx2-ffi/cxx/include/iox2/service_id.hpp b/iceoryx2-ffi/cxx/include/iox2/service_id.hpp index c38eb07b0..cbe88f0cb 100644 --- a/iceoryx2-ffi/cxx/include/iox2/service_id.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/service_id.hpp @@ -13,22 +13,30 @@ #ifndef IOX2_SERVICE_ID_HPP #define IOX2_SERVICE_ID_HPP -#include "iox/assertions_addendum.hpp" +#include "iox/string.hpp" +#include "iox2/internal/iceoryx2.hpp" namespace iox2 { /// Represents the unique if of a [`Service`]. class ServiceId { public: /// Returns the maximum string length of a [`ServiceId`] - auto max_len() -> uint64_t { - IOX_TODO(); - } + auto max_len() -> uint64_t; /// Returns the string value of the [`ServiceId`] - auto as_str() const -> const char* { - IOX_TODO(); - } + auto c_str() const -> const char*; + + private: + explicit ServiceId(const iox::string& value); + + template + friend class PortFactoryEvent; + template + friend class PortFactoryPublishSubscribe; + + iox::string m_value; }; + } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/service_name.hpp b/iceoryx2-ffi/cxx/include/iox2/service_name.hpp index 698ad99e3..e90c2749c 100644 --- a/iceoryx2-ffi/cxx/include/iox2/service_name.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/service_name.hpp @@ -44,6 +44,8 @@ class ServiceNameView { friend class Node; template friend class PortFactoryEvent; + template + friend class PortFactoryPublishSubscribe; explicit ServiceNameView(iox2_service_name_ptr ptr); iox2_service_name_ptr m_ptr = nullptr; diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config.hpp index 50e224d7a..3434a3660 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config.hpp @@ -20,6 +20,13 @@ namespace iox2 { /// Defines a common set of static service configuration details every service shares. class StaticConfig { public: + StaticConfig(const StaticConfig&) = delete; + StaticConfig(StaticConfig&& rhs) noexcept; + ~StaticConfig(); + + auto operator=(const StaticConfig&) -> StaticConfig& = delete; + auto operator=(StaticConfig&& rhs) noexcept -> StaticConfig&; + /// Returns the attributes of the [`Service`] auto attributes() const -> AttributeSetView; @@ -33,9 +40,12 @@ class StaticConfig { auto messaging_pattern() const -> MessagingPattern; private: + template + friend class Service; template friend auto list_callback(const iox2_static_config_t*, void*) -> iox2_callback_progression_e; explicit StaticConfig(iox2_static_config_t value); + void drop(); iox2_static_config_t m_value; }; diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp index e6a44a2e3..ff97b274e 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp @@ -15,6 +15,7 @@ #include "iox/duration.hpp" #include "iox/optional.hpp" +#include "iox2/attribute_set.hpp" #include "iox2/event_id.hpp" #include "iox2/iceoryx2.h" #include "iox2/internal/iceoryx2.hpp" diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp index 888336f14..11542ce5a 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp @@ -13,6 +13,7 @@ #ifndef IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP #define IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP +#include "iox2/attribute_set.hpp" #include "iox2/internal/iceoryx2.hpp" #include "iox2/message_type_details.hpp" diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 9a3da0b9b..6065a4ee3 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -123,7 +123,7 @@ inline auto Subscriber::id() const -> UniqueSubscriberId template inline auto Subscriber::buffer_size() const -> uint64_t { - IOX_TODO(); + return iox2_subscriber_buffer_size(&m_handle); } template diff --git a/iceoryx2-ffi/cxx/src/attribute_set.cpp b/iceoryx2-ffi/cxx/src/attribute_set.cpp index f2e71e37b..b1c1af984 100644 --- a/iceoryx2-ffi/cxx/src/attribute_set.cpp +++ b/iceoryx2-ffi/cxx/src/attribute_set.cpp @@ -11,7 +11,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/attribute_set.hpp" -#include "iox/assertions_addendum.hpp" +#include "iox/uninitialized_array.hpp" #include "iox2/internal/callback_context.hpp" namespace iox2 { @@ -23,7 +23,11 @@ auto get_key_values_callback(const char* value, iox2_callback_context context) - } } // namespace -AttributeSetView::AttributeSetView(iox2_attribute_set_h_ref handle) +///////////////////////////// +/// BEGIN: AttributeSetView +///////////////////////////// + +AttributeSetView::AttributeSetView(iox2_attribute_set_ptr handle) : m_handle { handle } { } @@ -35,13 +39,22 @@ auto AttributeSetView::at(const uint64_t index) const -> AttributeView { return AttributeView(iox2_attribute_set_at(m_handle, index)); } -auto AttributeSetView::get_key_value_len([[maybe_unused]] const Attribute::Key& key) const -> uint64_t { - IOX_TODO(); +auto AttributeSetView::get_key_value_len(const Attribute::Key& key) const -> uint64_t { + return iox2_attribute_set_get_key_value_len(m_handle, key.c_str()); } -auto AttributeSetView::get_key_value_at([[maybe_unused]] const Attribute::Key& key, [[maybe_unused]] const uint64_t idx) +auto AttributeSetView::get_key_value_at(const Attribute::Key& key, const uint64_t idx) -> iox::optional { - IOX_TODO(); + iox::UninitializedArray buffer; + bool has_value = false; + iox2_attribute_set_get_key_value_at( + m_handle, key.c_str(), idx, &buffer[0], Attribute::Value::capacity(), &has_value); + + if (!has_value) { + return iox::nullopt; + } + + return Attribute::Value(iox::TruncateToCapacity, &buffer[0]); } void AttributeSetView::get_key_values( @@ -49,9 +62,93 @@ void AttributeSetView::get_key_values( auto ctx = internal::ctx(callback); iox2_attribute_set_get_key_values(m_handle, key.c_str(), get_key_values_callback, static_cast(&ctx)); } + +auto AttributeSetView::to_owned() const -> AttributeSet { + iox2_attribute_set_h handle = nullptr; + iox2_attribute_set_new_clone(nullptr, m_handle, &handle); + return AttributeSet(handle); +} + +///////////////////////////// +/// END: AttributeSetView +///////////////////////////// + +///////////////////////////// +/// BEGIN: AttributeSet +///////////////////////////// +AttributeSet::AttributeSet(iox2_attribute_set_h handle) + : m_handle { handle } + , m_view { AttributeSetView(iox2_cast_attribute_set_ptr(handle)) } { +} + +AttributeSet::AttributeSet(AttributeSet&& rhs) noexcept + : m_handle { std::move(rhs.m_handle) } + , m_view { std::move(rhs.m_view) } { + rhs.m_handle = nullptr; + rhs.m_view.m_handle = nullptr; +} + +auto AttributeSet::operator=(AttributeSet&& rhs) noexcept -> AttributeSet& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + m_view = std::move(rhs.m_view); + + rhs.m_handle = nullptr; + rhs.m_view.m_handle = nullptr; + } + + return *this; +} + +AttributeSet::~AttributeSet() { + drop(); +} + +void AttributeSet::drop() { + if (m_handle != nullptr) { + iox2_attribute_set_drop(m_handle); + + m_handle = nullptr; + m_view.m_handle = nullptr; + } +} + +auto AttributeSet::len() const -> uint64_t { + return m_view.len(); +} + +auto AttributeSet::at(const uint64_t index) const -> AttributeView { + return m_view.at(index); +} + +auto AttributeSet::get_key_value_len(const Attribute::Key& key) const -> uint64_t { + return m_view.get_key_value_len(key); +} + +auto AttributeSet::get_key_value_at(const Attribute::Key& key, const uint64_t idx) -> iox::optional { + return m_view.get_key_value_at(key, idx); +} + +void AttributeSet::get_key_values(const Attribute::Key& key, + const iox::function& callback) const { + m_view.get_key_values(key, callback); +} +///////////////////////////// +/// END: AttributeSet +///////////////////////////// } // namespace iox2 auto operator<<(std::ostream& stream, const iox2::AttributeSetView& value) -> std::ostream& { + stream << "AttributeSetView { "; + for (uint64_t idx = 0; idx < value.len(); ++idx) { + auto attribute = value.at(idx); + stream << attribute; + } + return stream; +} + +auto operator<<(std::ostream& stream, const iox2::AttributeSet& value) -> std::ostream& { stream << "AttributeSet { "; for (uint64_t idx = 0; idx < value.len(); ++idx) { auto attribute = value.at(idx); diff --git a/iceoryx2-ffi/cxx/src/dynamic_config_event.cpp b/iceoryx2-ffi/cxx/src/dynamic_config_event.cpp new file mode 100644 index 000000000..cda357567 --- /dev/null +++ b/iceoryx2-ffi/cxx/src/dynamic_config_event.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/dynamic_config_event.hpp" + +namespace iox2 { +auto DynamicConfigEvent::number_of_listeners() const -> uint64_t { + return iox2_port_factory_event_dynamic_config_number_of_listeners(&m_handle); +} + +auto DynamicConfigEvent::number_of_notifiers() const -> uint64_t { + return iox2_port_factory_event_dynamic_config_number_of_notifiers(&m_handle); +} + +DynamicConfigEvent::DynamicConfigEvent(iox2_port_factory_event_h handle) + : m_handle { handle } { +} + + +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/dynamic_config_publish_subscribe.cpp b/iceoryx2-ffi/cxx/src/dynamic_config_publish_subscribe.cpp new file mode 100644 index 000000000..d2717665b --- /dev/null +++ b/iceoryx2-ffi/cxx/src/dynamic_config_publish_subscribe.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/dynamic_config_publish_subscribe.hpp" + +namespace iox2 { +auto DynamicConfigPublishSubscribe::number_of_publishers() const -> uint64_t { + return iox2_port_factory_pub_sub_dynamic_config_number_of_publishers(&m_handle); +} + +auto DynamicConfigPublishSubscribe::number_of_subscribers() const -> uint64_t { + return iox2_port_factory_pub_sub_dynamic_config_number_of_subscribers(&m_handle); +} + +DynamicConfigPublishSubscribe::DynamicConfigPublishSubscribe(iox2_port_factory_pub_sub_h handle) + : m_handle { handle } { +} +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/node.cpp b/iceoryx2-ffi/cxx/src/node.cpp index af60eb44e..e81c09fd0 100644 --- a/iceoryx2-ffi/cxx/src/node.cpp +++ b/iceoryx2-ffi/cxx/src/node.cpp @@ -83,57 +83,13 @@ auto Node::service_builder(const ServiceName& name) const -> ServiceBuilder { &m_handle, name.as_view().m_ptr }; } -template -// NOLINTBEGIN(readability-function-size) -auto list_callback(iox2_node_state_e node_state, - iox2_node_id_ptr node_id_ptr, - const char* executable, - iox2_node_name_ptr node_name, - iox2_config_ptr config, - iox2_callback_context context) -> iox2_callback_progression_e { - auto node_details = [&] { - if (node_id_ptr == nullptr || config == nullptr) { - return iox::optional(); - } - - return iox::optional(NodeDetails { - iox::FileName::create(iox::string(iox::TruncateToCapacity, executable)) - .expect("The executable file name is always valid."), - NodeNameView { node_name }.to_owned(), - Config {} }); - }(); - - iox2_node_id_h node_id_handle = nullptr; - iox2_node_id_clone_from_ptr(nullptr, node_id_ptr, &node_id_handle); - NodeId node_id { node_id_handle }; - - auto node_state_object = [&] { - switch (node_state) { - case iox2_node_state_e_ALIVE: - return NodeState { AliveNodeView { node_id, node_details } }; - case iox2_node_state_e_DEAD: - return NodeState { DeadNodeView { AliveNodeView { node_id, node_details } } }; - case iox2_node_state_e_UNDEFINED: - return NodeState { iox2_node_state_e_UNDEFINED, node_id }; - case iox2_node_state_e_INACCESSIBLE: - return NodeState { iox2_node_state_e_INACCESSIBLE, node_id }; - } - - IOX_UNREACHABLE(); - }(); - - auto* callback = internal::ctx_cast)>>(context); - return iox::into(callback->value()(node_state_object)); -} -// NOLINTEND(readability-function-size) - template auto Node::list(ConfigView config, const iox::function)>& callback) -> iox::expected { auto ctx = internal::ctx(callback); - const auto ret_val = - iox2_node_list(iox::into(T), config.m_ptr, list_callback, static_cast(&ctx)); + const auto ret_val = iox2_node_list( + iox::into(T), config.m_ptr, internal::list_callback, static_cast(&ctx)); if (ret_val == IOX2_OK) { return iox::ok(); diff --git a/iceoryx2-ffi/cxx/src/port_factory_event.cpp b/iceoryx2-ffi/cxx/src/port_factory_event.cpp index be7e2fd03..a304be2c7 100644 --- a/iceoryx2-ffi/cxx/src/port_factory_event.cpp +++ b/iceoryx2-ffi/cxx/src/port_factory_event.cpp @@ -11,8 +11,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/port_factory_event.hpp" -#include "iox/assertions_addendum.hpp" +#include "iox/uninitialized_array.hpp" #include "iox2/iceoryx2.h" +#include "iox2/internal/callback_context.hpp" namespace iox2 { template @@ -56,8 +57,11 @@ auto PortFactoryEvent::name() const -> ServiceNameView { } template -auto PortFactoryEvent::service_id() const -> const ServiceId& { - IOX_TODO(); +auto PortFactoryEvent::service_id() const -> ServiceId { + iox::UninitializedArray buffer; + iox2_port_factory_event_service_id(&m_handle, &buffer[0], IOX2_SERVICE_ID_LENGTH); + + return ServiceId(iox::string(iox::TruncateToCapacity, &buffer[0])); } template @@ -74,14 +78,22 @@ auto PortFactoryEvent::static_config() const -> StaticConfigEvent { } template -auto PortFactoryEvent::dynamic_config() const -> const DynamicConfigEvent& { - IOX_TODO(); +auto PortFactoryEvent::dynamic_config() const -> DynamicConfigEvent { + return DynamicConfigEvent(m_handle); } template -auto PortFactoryEvent::nodes([[maybe_unused]] const iox::function)>& callback) const +auto PortFactoryEvent::nodes(const iox::function)>& callback) const -> iox::expected { - IOX_TODO(); + auto ctx = internal::ctx(callback); + + const auto ret_val = iox2_port_factory_event_nodes(&m_handle, internal::list_callback, static_cast(&ctx)); + + if (ret_val == IOX2_OK) { + return iox::ok(); + } + + return iox::err(iox::into(ret_val)); } template diff --git a/iceoryx2-ffi/cxx/src/service.cpp b/iceoryx2-ffi/cxx/src/service.cpp index 9df8a59d0..ed95dc618 100644 --- a/iceoryx2-ffi/cxx/src/service.cpp +++ b/iceoryx2-ffi/cxx/src/service.cpp @@ -38,11 +38,29 @@ auto Service::does_exist(const ServiceName& service_name, } template -auto Service::details([[maybe_unused]] const ServiceName& service_name, - [[maybe_unused]] const ConfigView config, - [[maybe_unused]] const MessagingPattern messaging_pattern) +auto Service::details(const ServiceName& service_name, + const ConfigView config, + const MessagingPattern messaging_pattern) -> iox::expected>, ServiceDetailsError> { - IOX_TODO(); + iox2_static_config_t raw_static_config; + bool does_exist = false; + + auto result = iox2_service_details(iox::into(S), + service_name.as_view().m_ptr, + config.m_ptr, + iox::into(messaging_pattern), + &raw_static_config, + &does_exist); + + if (result != IOX2_OK) { + return iox::err(iox::into(result)); + } + + if (!does_exist) { + return iox::ok(iox::optional>()); + } + + return iox::ok(iox::optional(ServiceDetails { StaticConfig(raw_static_config) })); } template diff --git a/iceoryx2-ffi/cxx/src/service_builder_event.cpp b/iceoryx2-ffi/cxx/src/service_builder_event.cpp index b098a5512..1ddacea99 100644 --- a/iceoryx2-ffi/cxx/src/service_builder_event.cpp +++ b/iceoryx2-ffi/cxx/src/service_builder_event.cpp @@ -11,7 +11,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/service_builder_event.hpp" -#include "iox/assertions_addendum.hpp" namespace iox2 { template @@ -56,8 +55,9 @@ void ServiceBuilderEvent::set_parameters() { .or_else([&]() { iox2_service_builder_event_disable_deadline(&m_handle); }); } - m_max_nodes.and_then([](auto) { IOX_TODO(); }); - m_event_id_max_value.and_then([](auto) { IOX_TODO(); }); + m_max_nodes.and_then([&](auto value) { iox2_service_builder_event_set_max_nodes(&m_handle, value); }); + m_event_id_max_value.and_then( + [&](auto value) { iox2_service_builder_event_set_event_id_max_value(&m_handle, value); }); } template diff --git a/iceoryx2-ffi/cxx/src/service_id.cpp b/iceoryx2-ffi/cxx/src/service_id.cpp new file mode 100644 index 000000000..b63230f8c --- /dev/null +++ b/iceoryx2-ffi/cxx/src/service_id.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/service_id.hpp" + +namespace iox2 { +auto ServiceId::max_len() -> uint64_t { + return IOX2_SERVICE_ID_LENGTH; +} + +auto ServiceId::c_str() const -> const char* { + return m_value.c_str(); +} + +ServiceId::ServiceId(const iox::string& value) + : m_value { value } { +} +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/static_config.cpp b/iceoryx2-ffi/cxx/src/static_config.cpp index 19b82137d..344650f99 100644 --- a/iceoryx2-ffi/cxx/src/static_config.cpp +++ b/iceoryx2-ffi/cxx/src/static_config.cpp @@ -11,7 +11,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/static_config.hpp" -#include "iox/assertions_addendum.hpp" #include "iox/into.hpp" namespace iox2 { @@ -19,8 +18,33 @@ StaticConfig::StaticConfig(iox2_static_config_t value) : m_value { value } { } +StaticConfig::StaticConfig(StaticConfig&& rhs) noexcept + : m_value { std::move(rhs.m_value) } { + rhs.m_value.attributes = nullptr; +} + +auto StaticConfig::operator=(StaticConfig&& rhs) noexcept -> StaticConfig& { + if (this != &rhs) { + drop(); + m_value = std::move(rhs.m_value); + rhs.m_value.attributes = nullptr; + } + return *this; +} + +StaticConfig::~StaticConfig() { + drop(); +} + +void StaticConfig::drop() { + if (m_value.attributes != nullptr) { + iox2_attribute_set_drop(m_value.attributes); + m_value.attributes = nullptr; + } +} + auto StaticConfig::attributes() const -> AttributeSetView { - IOX_TODO(); + return AttributeSetView(iox2_cast_attribute_set_ptr(m_value.attributes)); } auto StaticConfig::id() const -> const char* { diff --git a/iceoryx2-ffi/cxx/tests/CMakeLists.txt b/iceoryx2-ffi/cxx/tests/CMakeLists.txt index 6fadc0284..9fcc759a6 100644 --- a/iceoryx2-ffi/cxx/tests/CMakeLists.txt +++ b/iceoryx2-ffi/cxx/tests/CMakeLists.txt @@ -23,6 +23,7 @@ file(GLOB TEST_FILES src/*.cpp) add_executable(${PROJECT_NAME} ${TEST_FILES}) target_link_libraries(${PROJECT_NAME} iceoryx2-cxx::static-lib-cxx GTest::gtest GTest::gmock) +target_compile_options(${PROJECT_NAME} PRIVATE $<$:/bigobj>) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 diff --git a/iceoryx2-ffi/cxx/tests/src/attribute_tests.cpp b/iceoryx2-ffi/cxx/tests/src/attribute_tests.cpp index f96ec6e1e..6c3b0c413 100644 --- a/iceoryx2-ffi/cxx/tests/src/attribute_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/attribute_tests.cpp @@ -118,4 +118,60 @@ TEST(AttributeSet, all_key_values_can_be_acquired) { return CallbackProgression::Continue; }); } + +TEST(AttributeSet, get_key_value_len_works) { + auto empty_key = Attribute::Key("fuu"); + auto key = Attribute::Key("whatever"); + auto value_1 = Attribute::Value("you"); + auto value_2 = Attribute::Value("want"); + + auto attribute_specifer = AttributeSpecifier().define(key, value_1).define(key, value_2); + auto attributes = attribute_specifer.attributes(); + + ASSERT_THAT(attributes.get_key_value_len(key), Eq(2)); + ASSERT_THAT(attributes.get_key_value_len(empty_key), Eq(0)); +} + +//NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by ASSERT_THAT +TEST(AttributeSet, get_key_value_at_works) { + auto key = Attribute::Key("schmu whatever"); + auto value_1 = Attribute::Value("fuu you"); + auto value_2 = Attribute::Value("blue want"); + + auto attribute_specifer = AttributeSpecifier().define(key, value_1).define(key, value_2); + auto attributes = attribute_specifer.attributes(); + + auto v_1 = attributes.get_key_value_at(key, 0); + auto v_2 = attributes.get_key_value_at(key, 1); + auto v_3 = attributes.get_key_value_at(key, 2); + + ASSERT_THAT(v_1.has_value(), Eq(true)); + ASSERT_THAT(v_2.has_value(), Eq(true)); + ASSERT_THAT(v_3.has_value(), Eq(false)); + + if (v_1->size() == value_1.size()) { + ASSERT_THAT(v_1.value().c_str(), StrEq(value_1.c_str())); + ASSERT_THAT(v_2.value().c_str(), StrEq(value_2.c_str())); + } else { + ASSERT_THAT(v_2.value().c_str(), StrEq(value_1.c_str())); + ASSERT_THAT(v_1.value().c_str(), StrEq(value_2.c_str())); + } +} +//NOLINTEND(readability-function-cognitive-complexity) + +TEST(AttributeSet, to_owned_works) { + auto key = Attribute::Key("your mind becomes a galaxy"); + auto value_1 = Attribute::Value("shiny and bright"); + auto value_2 = Attribute::Value("with spice aroma"); + + auto attribute_specifer = AttributeSpecifier().define(key, value_1).define(key, value_2); + auto attributes = attribute_specifer.attributes(); + auto attributes_owned = attributes.to_owned(); + + ASSERT_THAT(attributes_owned.len(), Eq(2)); + ASSERT_THAT(attributes_owned.at(0).key(), Eq(key)); + ASSERT_THAT(attributes_owned.at(1).key(), Eq(key)); + ASSERT_THAT(attributes_owned.at(0).value(), Eq(value_1)); + ASSERT_THAT(attributes_owned.at(1).value(), Eq(value_2)); +} } // namespace diff --git a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp index 02f5cfa81..2c6705800 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp @@ -92,6 +92,8 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; constexpr uint64_t NUMBER_OF_LISTENERS = 7; + constexpr uint64_t NUMBER_OF_NODES = 8; + constexpr uint64_t MAX_EVENT_ID_VALUE = 9; const auto create_event_id = EventId(12); const auto dropped_event_id = EventId(13); const auto dead_event_id = EventId(14); @@ -103,6 +105,8 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { .event() .max_notifiers(NUMBER_OF_NOTIFIERS) .max_listeners(NUMBER_OF_LISTENERS) + .max_nodes(NUMBER_OF_NODES) + .event_id_max_value(MAX_EVENT_ID_VALUE) .notifier_created_event(create_event_id) .notifier_dropped_event(dropped_event_id) .notifier_dead_event(dead_event_id) @@ -113,6 +117,8 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { ASSERT_THAT(static_config.max_notifiers(), Eq(NUMBER_OF_NOTIFIERS)); ASSERT_THAT(static_config.max_listeners(), Eq(NUMBER_OF_LISTENERS)); + ASSERT_THAT(static_config.max_nodes(), Eq(NUMBER_OF_NODES)); + ASSERT_THAT(static_config.event_id_max_value(), Eq(MAX_EVENT_ID_VALUE)); ASSERT_THAT(static_config.notifier_created_event(), Eq(iox::optional(create_event_id))); ASSERT_THAT(static_config.notifier_dropped_event(), Eq(iox::optional(dropped_event_id))); ASSERT_THAT(static_config.notifier_dead_event(), Eq(iox::optional(dead_event_id))); @@ -570,4 +576,79 @@ TYPED_TEST(ServiceEventTest, when_deadline_is_not_missed_notification_works) { ASSERT_THAT(result.has_value(), Eq(true)); ASSERT_THAT(listener.try_wait_one().expect("").has_value(), Eq(true)); } + +TYPED_TEST(ServiceEventTest, number_of_listener_notifier_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + const auto service_name = iox2_testing::generate_service_name(); + auto node = NodeBuilder().create().expect(""); + + auto service = node.service_builder(service_name).event().create().expect(""); + + ASSERT_THAT(service.dynamic_config().number_of_listeners(), Eq(0)); + ASSERT_THAT(service.dynamic_config().number_of_notifiers(), Eq(0)); + { + auto listener = service.listener_builder().create().expect(""); + ASSERT_THAT(service.dynamic_config().number_of_listeners(), Eq(1)); + ASSERT_THAT(service.dynamic_config().number_of_notifiers(), Eq(0)); + + auto notifier = service.notifier_builder().create().expect(""); + ASSERT_THAT(service.dynamic_config().number_of_listeners(), Eq(1)); + ASSERT_THAT(service.dynamic_config().number_of_notifiers(), Eq(1)); + } + ASSERT_THAT(service.dynamic_config().number_of_listeners(), Eq(0)); + ASSERT_THAT(service.dynamic_config().number_of_notifiers(), Eq(0)); +} + +TYPED_TEST(ServiceEventTest, service_id_is_unique_per_service) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + const auto service_name_1 = iox2_testing::generate_service_name(); + const auto service_name_2 = iox2_testing::generate_service_name(); + auto node = NodeBuilder().create().expect(""); + + auto service_1_create = node.service_builder(service_name_1).event().create().expect(""); + auto service_1_open = node.service_builder(service_name_1).event().open().expect(""); + auto service_2 = node.service_builder(service_name_2).event().create().expect(""); + + ASSERT_THAT(service_1_create.service_id().c_str(), StrEq(service_1_open.service_id().c_str())); + ASSERT_THAT(service_1_create.service_id().c_str(), Not(StrEq(service_2.service_id().c_str()))); +} + +//NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by ASSERT_THAT +TYPED_TEST(ServiceEventTest, list_service_nodes_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto node_name_1 = NodeName::create("Nala and The HypnoToad").expect(""); + const auto node_name_2 = NodeName::create("Can they be friends?").expect(""); + const auto service_name = iox2_testing::generate_service_name(); + + auto node_1 = NodeBuilder().name(node_name_1).create().expect(""); + auto node_2 = NodeBuilder().name(node_name_2).create().expect(""); + + auto sut_1 = node_1.service_builder(service_name).event().create().expect(""); + auto sut_2 = node_2.service_builder(service_name).event().open().expect(""); + + auto counter = 0; + auto verify_node = [&](const AliveNodeView& node_view) { + counter++; + if (node_view.id() == node_1.id()) { + ASSERT_THAT(node_view.details()->name().to_string().c_str(), StrEq(node_1.name().to_string().c_str())); + } else { + ASSERT_THAT(node_view.details()->name().to_string().c_str(), StrEq(node_2.name().to_string().c_str())); + } + }; + + auto result = sut_1.nodes([&](auto node_state) -> CallbackProgression { + node_state.alive(verify_node); + + node_state.dead([](const auto&) { ASSERT_TRUE(false); }); + node_state.inaccessible([](const auto&) { ASSERT_TRUE(false); }); + node_state.undefined([](const auto&) { ASSERT_TRUE(false); }); + + return CallbackProgression::Continue; + }); + + ASSERT_THAT(result.has_value(), Eq(true)); + ASSERT_THAT(counter, Eq(2)); +} +//NOLINTEND(readability-function-cognitive-complexity) } // namespace diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 087f9df29..385f546c0 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -56,6 +56,56 @@ TYPED_TEST(ServicePublishSubscribeTest, created_service_does_exist) { Service::does_exist(service_name, Config::global_config(), MessagingPattern::Event).expect("")); } +TYPED_TEST(ServicePublishSubscribeTest, service_name_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto service_name = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + auto sut = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + ASSERT_THAT(sut.name().to_string().c_str(), StrEq(service_name.to_string().c_str())); +} + +//NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by ASSERT_THAT +TYPED_TEST(ServicePublishSubscribeTest, list_service_nodes_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto node_name_1 = NodeName::create("nala is hungry").expect(""); + const auto node_name_2 = NodeName::create("maybe octo-wolf can help?").expect(""); + const auto service_name = iox2_testing::generate_service_name(); + + auto node_1 = NodeBuilder().name(node_name_1).create().expect(""); + auto node_2 = NodeBuilder().name(node_name_2).create().expect(""); + + auto sut_1 = node_1.service_builder(service_name).template publish_subscribe().create().expect(""); + auto sut_2 = node_2.service_builder(service_name).template publish_subscribe().open().expect(""); + + auto counter = 0; + auto verify_node = [&](const AliveNodeView& node_view) { + counter++; + if (node_view.id() == node_1.id()) { + ASSERT_THAT(node_view.details()->name().to_string().c_str(), StrEq(node_1.name().to_string().c_str())); + } else { + ASSERT_THAT(node_view.details()->name().to_string().c_str(), StrEq(node_2.name().to_string().c_str())); + } + }; + + auto result = sut_1.nodes([&](auto node_state) -> CallbackProgression { + node_state.alive(verify_node); + + node_state.dead([](const auto&) { ASSERT_TRUE(false); }); + node_state.inaccessible([](const auto&) { ASSERT_TRUE(false); }); + node_state.undefined([](const auto&) { ASSERT_TRUE(false); }); + + return CallbackProgression::Continue; + }); + + ASSERT_THAT(result.has_value(), Eq(true)); + ASSERT_THAT(counter, Eq(2)); +} +//NOLINTEND(readability-function-cognitive-complexity) + TYPED_TEST(ServicePublishSubscribeTest, creating_existing_service_fails) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; @@ -300,6 +350,32 @@ TYPED_TEST(ServicePublishSubscribeTest, loan_slice_send_receive_works) { ASSERT_THAT(iterations, Eq(SLICE_MAX_LENGTH)); } +TYPED_TEST(ServicePublishSubscribeTest, number_of_publishers_subscribers_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto service_name = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + ASSERT_THAT(service.dynamic_config().number_of_publishers(), Eq(0)); + ASSERT_THAT(service.dynamic_config().number_of_subscribers(), Eq(0)); + + { + auto sut_publisher = service.publisher_builder().create().expect(""); + ASSERT_THAT(service.dynamic_config().number_of_publishers(), Eq(1)); + ASSERT_THAT(service.dynamic_config().number_of_subscribers(), Eq(0)); + + auto sut_subscriber = service.subscriber_builder().create().expect(""); + ASSERT_THAT(service.dynamic_config().number_of_publishers(), Eq(1)); + ASSERT_THAT(service.dynamic_config().number_of_subscribers(), Eq(1)); + } + + ASSERT_THAT(service.dynamic_config().number_of_publishers(), Eq(0)); + ASSERT_THAT(service.dynamic_config().number_of_subscribers(), Eq(0)); +} + + // NOLINTBEGIN(readability-function-cognitive-complexity) : Cognitive complexity of 26 (+1) is OK. Test case is complex. TYPED_TEST(ServicePublishSubscribeTest, loan_slice_uninit_send_receive_works) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; @@ -510,6 +586,12 @@ TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { ASSERT_THAT(static_config.message_type_details().payload().size(), Eq(sizeof(uint64_t))); ASSERT_THAT(static_config.message_type_details().payload().alignment(), Eq(alignof(uint64_t))); ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq("u64")); + + auto subscriber = service.subscriber_builder().create().expect(""); + ASSERT_THAT(subscriber.buffer_size(), Eq(SUBSCRIBER_MAX_BUFFER_SIZE)); + + auto subscriber_2 = service.subscriber_builder().buffer_size(1).create().expect(""); + ASSERT_THAT(subscriber_2.buffer_size(), Eq(1)); } TYPED_TEST(ServicePublishSubscribeTest, safe_overflow_can_be_set) { @@ -1275,5 +1357,20 @@ TYPED_TEST(ServicePublishSubscribeTest, PayloadTypeNameIsSetToInnerTypeNameIfPro auto static_config = service.static_config(); ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq("Payload")); } + +TYPED_TEST(ServicePublishSubscribeTest, service_id_is_unique_per_service) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + const auto service_name_1 = iox2_testing::generate_service_name(); + const auto service_name_2 = iox2_testing::generate_service_name(); + auto node = NodeBuilder().create().expect(""); + + auto service_1_create = + node.service_builder(service_name_1).template publish_subscribe().create().expect(""); + auto service_1_open = node.service_builder(service_name_1).template publish_subscribe().open().expect(""); + auto service_2 = node.service_builder(service_name_2).template publish_subscribe().create().expect(""); + + ASSERT_THAT(service_1_create.service_id().c_str(), StrEq(service_1_open.service_id().c_str())); + ASSERT_THAT(service_1_create.service_id().c_str(), Not(StrEq(service_2.service_id().c_str()))); +} // END tests for customizable payload and user header type name } // namespace diff --git a/iceoryx2-ffi/cxx/tests/src/service_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_tests.cpp new file mode 100644 index 000000000..af5355a5b --- /dev/null +++ b/iceoryx2-ffi/cxx/tests/src/service_tests.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/attribute_specifier.hpp" +#include "iox2/node.hpp" +#include "iox2/service.hpp" + +#include "test.hpp" + +namespace { +using namespace iox2; + +template +class ServiceTest : public ::testing::Test { + public: + static constexpr ServiceType TYPE = T::TYPE; +}; + +TYPED_TEST_SUITE(ServiceTest, iox2_testing::ServiceTypes, ); + +TYPED_TEST(ServiceTest, does_exist_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto service_name = iox2_testing::generate_service_name(); + + ASSERT_FALSE( + Service::does_exist(service_name, Config::global_config(), MessagingPattern::PublishSubscribe) + .expect("")); + ASSERT_FALSE( + Service::does_exist(service_name, Config::global_config(), MessagingPattern::Event).expect("")); + + auto node = NodeBuilder().create().expect(""); + + { + auto sut = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + ASSERT_TRUE( + Service::does_exist(service_name, Config::global_config(), MessagingPattern::PublishSubscribe) + .expect("")); + ASSERT_FALSE(Service::does_exist(service_name, Config::global_config(), MessagingPattern::Event) + .expect("")); + } + + ASSERT_FALSE( + Service::does_exist(service_name, Config::global_config(), MessagingPattern::PublishSubscribe) + .expect("")); + ASSERT_FALSE( + Service::does_exist(service_name, Config::global_config(), MessagingPattern::Event).expect("")); +} + +TYPED_TEST(ServiceTest, list_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto service_name_1 = iox2_testing::generate_service_name(); + const auto service_name_2 = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + + auto sut_1 = node.service_builder(service_name_1).template publish_subscribe().create().expect(""); + auto sut_2 = node.service_builder(service_name_2).event().create().expect(""); + + //NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by EXPECT_THAT + auto verify = [&](auto details) -> CallbackProgression { + if (details.static_details.messaging_pattern() == MessagingPattern::PublishSubscribe) { + EXPECT_THAT(details.static_details.name(), StrEq(service_name_1.to_string().c_str())); + EXPECT_THAT(details.static_details.id(), StrEq(sut_1.service_id().c_str())); + } else { + EXPECT_THAT(details.static_details.name(), StrEq(service_name_2.to_string().c_str())); + EXPECT_THAT(details.static_details.id(), StrEq(sut_2.service_id().c_str())); + } + + return CallbackProgression::Continue; + }; + //NOLINTEND(readability-function-cognitive-complexity) + + auto result = Service::list(Config::global_config(), verify); + + ASSERT_THAT(result.has_value(), Eq(true)); +} + +TYPED_TEST(ServiceTest, list_works_with_attributes) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + auto key_1 = Attribute::Key("do elephants like strawberries?"); + auto value_1 = Attribute::Value("do strawberries like elephants?"); + auto key_2 = Attribute::Key("the berry of the straw"); + auto value_2 = Attribute::Value("has left the field!"); + + + const auto service_name_1 = iox2_testing::generate_service_name(); + const auto service_name_2 = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + + auto sut_1 = node.service_builder(service_name_1) + .template publish_subscribe() + .create_with_attributes(AttributeSpecifier().define(key_1, value_1).define(key_2, value_2)) + .expect(""); + auto sut_2 = node.service_builder(service_name_2).event().create().expect(""); + + //NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by EXPECT_THAT + auto verify = [&](auto details) -> CallbackProgression { + if (details.static_details.messaging_pattern() == MessagingPattern::PublishSubscribe) { + EXPECT_THAT(details.static_details.name(), StrEq(service_name_1.to_string().c_str())); + EXPECT_THAT(details.static_details.id(), StrEq(sut_1.service_id().c_str())); + + auto counter = 0; + details.static_details.attributes().get_key_values(key_1, [&](auto& value) -> CallbackProgression { + EXPECT_THAT(value.c_str(), StrEq(value_1.c_str())); + counter++; + return CallbackProgression::Continue; + }); + EXPECT_THAT(counter, Eq(1)); + + counter = 0; + details.static_details.attributes().get_key_values(key_2, [&](auto& value) -> CallbackProgression { + EXPECT_THAT(value.c_str(), StrEq(value_2.c_str())); + counter++; + return CallbackProgression::Continue; + }); + EXPECT_THAT(counter, Eq(1)); + } else { + EXPECT_THAT(details.static_details.name(), StrEq(service_name_2.to_string().c_str())); + EXPECT_THAT(details.static_details.id(), StrEq(sut_2.service_id().c_str())); + } + + return CallbackProgression::Continue; + }; + //NOLINTEND(readability-function-cognitive-complexity) + + auto result = Service::list(Config::global_config(), verify); + + ASSERT_THAT(result.has_value(), Eq(true)); +} + +//NOLINTBEGIN(readability-function-cognitive-complexity), false positive caused by ASSERT_THAT +TYPED_TEST(ServiceTest, details_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + auto key_1 = Attribute::Key("gimme a strawberries?"); + auto value_1 = Attribute::Value("i want a strawberry!"); + auto key_2 = Attribute::Key("it makes me immortal"); + auto value_2 = Attribute::Value("or at least sticky"); + + + const auto service_name_1 = iox2_testing::generate_service_name(); + const auto service_name_2 = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + + auto sut = node.service_builder(service_name_1) + .template publish_subscribe() + .create_with_attributes(AttributeSpecifier().define(key_1, value_1).define(key_2, value_2)) + .expect(""); + + auto result = + Service::details(service_name_1, Config::global_config(), MessagingPattern::PublishSubscribe); + + ASSERT_THAT(result.has_value(), Eq(true)); + ASSERT_THAT(result->has_value(), Eq(true)); + + ASSERT_THAT(result.value()->static_details.name(), StrEq(service_name_1.to_string().c_str())); + ASSERT_THAT(result.value()->static_details.name(), StrEq(service_name_1.to_string().c_str())); + + auto counter = 0; + result.value()->static_details.attributes().get_key_values(key_1, [&](auto& value) -> CallbackProgression { + EXPECT_THAT(value.c_str(), StrEq(value_1.c_str())); + counter++; + return CallbackProgression::Continue; + }); + EXPECT_THAT(counter, Eq(1)); + + counter = 0; + result.value()->static_details.attributes().get_key_values(key_2, [&](auto& value) -> CallbackProgression { + EXPECT_THAT(value.c_str(), StrEq(value_2.c_str())); + counter++; + return CallbackProgression::Continue; + }); + EXPECT_THAT(counter, Eq(1)); + + result = Service::details(service_name_1, Config::global_config(), MessagingPattern::Event); + ASSERT_THAT(result.has_value(), Eq(true)); + ASSERT_THAT(result->has_value(), Eq(false)); + + result = + Service::details(service_name_2, Config::global_config(), MessagingPattern::PublishSubscribe); + ASSERT_THAT(result.has_value(), Eq(true)); + ASSERT_THAT(result->has_value(), Eq(false)); +} +//NOLINTEND(readability-function-cognitive-complexity) +} // namespace diff --git a/iceoryx2-ffi/ffi/cbindgen.toml b/iceoryx2-ffi/ffi/cbindgen.toml index a682fa831..558836aa1 100644 --- a/iceoryx2-ffi/ffi/cbindgen.toml +++ b/iceoryx2-ffi/ffi/cbindgen.toml @@ -81,6 +81,7 @@ after_includes = """ #endif // forward declarations of renamed types +typedef struct iox2_attribute_set_ptr_t iox2_attribute_set_ptr_t; typedef struct iox2_config_ptr_t iox2_config_ptr_t; typedef struct iox2_node_id_ptr_t iox2_node_id_ptr_t; typedef struct iox2_node_name_ptr_t iox2_node_name_ptr_t; @@ -148,6 +149,7 @@ renaming_overrides_prefixing = false # Table of name conversions to apply to item names (lhs becomes rhs) [export.rename] +"AttributeSet" = "iox2_attribute_set_ptr_t" "Config" = "iox2_config_ptr_t" "NodeId" = "iox2_node_id_ptr_t" "NodeName" = "iox2_node_name_ptr_t" diff --git a/iceoryx2-ffi/ffi/src/api/attribute_set.rs b/iceoryx2-ffi/ffi/src/api/attribute_set.rs index e2e25ed7e..1a0b9bb4c 100644 --- a/iceoryx2-ffi/ffi/src/api/attribute_set.rs +++ b/iceoryx2-ffi/ffi/src/api/attribute_set.rs @@ -13,25 +13,77 @@ #![allow(non_camel_case_types)] use core::ffi::{c_char, CStr}; +use iceoryx2_bb_elementary::static_assert::*; extern crate alloc; use alloc::ffi::CString; use iceoryx2::service::attribute::{Attribute, AttributeSet}; use iceoryx2_bb_elementary::CallbackProgression; +use iceoryx2_ffi_macros::iceoryx2_ffi; -use super::{iox2_attribute_h_ref, iox2_callback_context, iox2_callback_progression_e}; +use super::{ + iox2_attribute_h_ref, iox2_callback_context, iox2_callback_progression_e, AssertNonNullHandle, + HandleToType, +}; // BEGIN types definition +#[repr(C)] +#[repr(align(8))] // alignment of Option +pub struct iox2_attribute_set_storage_t { + internal: [u8; 24], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(AttributeSet)] +pub struct iox2_attribute_set_t { + pub value: iox2_attribute_set_storage_t, + deleter: fn(*mut iox2_attribute_set_t), +} + pub struct iox2_attribute_set_h_t; +/// The owning handle for `iox2_attribute_set_t`. Passing the handle to an function transfers the ownership. +pub type iox2_attribute_set_h = *mut iox2_attribute_set_h_t; + +/// The non-owning handle for `iox2_attribute_set_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_attribute_set_h_ref = *const iox2_attribute_set_h; + +// NOTE check the README.md for using opaque types with renaming +/// The immutable pointer to the underlying `AttributeSet` +pub type iox2_attribute_set_ptr = *const AttributeSet; +/// The mutable pointer to the underlying `AttributeSet` +pub type iox2_attribute_set_ptr_mut = *mut AttributeSet; -impl iox2_attribute_set_h_t { - pub(crate) unsafe fn underlying_type(&self) -> &AttributeSet { - &*(self as *const iox2_attribute_set_h_t).cast() +impl AssertNonNullHandle for iox2_attribute_set_h { + fn assert_non_null(self) { + debug_assert!(!self.is_null()); } } -pub type iox2_attribute_set_h_ref = *const iox2_attribute_set_h_t; +impl AssertNonNullHandle for iox2_attribute_set_h_ref { + fn assert_non_null(self) { + debug_assert!(!self.is_null()); + unsafe { + debug_assert!(!(*self).is_null()); + } + } +} + +impl HandleToType for iox2_attribute_set_h { + type Target = *mut iox2_attribute_set_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_attribute_set_h_ref { + type Target = *mut iox2_attribute_set_t; + + fn as_type(self) -> Self::Target { + unsafe { *self as *mut _ as _ } + } +} pub type iox2_attribute_set_get_callback = extern "C" fn(*const c_char, iox2_callback_context) -> iox2_callback_progression_e; @@ -40,17 +92,90 @@ pub type iox2_attribute_set_get_callback = // BEGIN C API +/// This function create a new attribute_set by cloning an already existing one! +/// +/// # Safety +/// +/// * `struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_attribute_set_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `source_ptr` - Must be valid pointer to a [`iox2_attribute_set_ptr`]. +/// * `handle_ptr` - An uninitialized or dangling [`iox2_attribute_set_h`] handle which will be initialized by this function call. +/// +#[no_mangle] +pub unsafe extern "C" fn iox2_attribute_set_new_clone( + struct_ptr: *mut iox2_attribute_set_t, + source_ptr: iox2_attribute_set_ptr, + handle_ptr: *mut iox2_attribute_set_h, +) { + debug_assert!(!handle_ptr.is_null()); + debug_assert!(!source_ptr.is_null()); + + *handle_ptr = core::ptr::null_mut(); + + let mut struct_ptr = struct_ptr; + fn no_op(_: *mut iox2_attribute_set_t) {} + let mut deleter: fn(*mut iox2_attribute_set_t) = no_op; + if struct_ptr.is_null() { + struct_ptr = iox2_attribute_set_t::alloc(); + deleter = iox2_attribute_set_t::dealloc; + } + debug_assert!(!struct_ptr.is_null()); + + unsafe { + (*struct_ptr).deleter = deleter; + } + + unsafe { + (*struct_ptr).value.init((*source_ptr).clone()); + } + + *handle_ptr = (*struct_ptr).as_handle(); +} + +/// This function needs to be called to destroy the attribute set! +/// +/// # Safety +/// +/// * `handle` - A valid [`iox2_attribute_set_h`] created with [`iox2_attribute_set_new_clone()`]. +/// * The `handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_attribute_set_t`] can be re-used with a call to [`iox2_attribute_set_new_clone()`]! +#[no_mangle] +pub unsafe extern "C" fn iox2_attribute_set_drop(handle: iox2_attribute_set_h) { + debug_assert!(!handle.is_null()); + + let attribute_set = &mut *handle.as_type(); + + core::ptr::drop_in_place(attribute_set.value.as_option_mut()); + (attribute_set.deleter)(attribute_set); +} + +/// This function casts a [`iox2_attribute_set_h`] into a [`iox2_attribute_set_ptr`] +/// +/// Returns a [`iox2_attribute_set_ptr`] +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_attribute_set_new_clone()`] +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_attribute_set_ptr( + handle: iox2_attribute_set_h, +) -> iox2_attribute_set_ptr { + debug_assert!(!handle.is_null()); + + (*handle.as_type()).value.as_ref() +} + /// Returns the length of the attribute set. /// /// # Safety /// /// * The `handle` must be a valid handle. #[no_mangle] -pub unsafe extern "C" fn iox2_attribute_set_len(handle: iox2_attribute_set_h_ref) -> usize { +pub unsafe extern "C" fn iox2_attribute_set_len(handle: iox2_attribute_set_ptr) -> usize { debug_assert!(!handle.is_null()); - let attribute_set = (*handle).underlying_type(); - attribute_set.iter().len() + (*handle).iter().len() } /// Returns a [`iox2_attribute_h_ref`] to the attribute stored at the provided index. @@ -61,14 +186,80 @@ pub unsafe extern "C" fn iox2_attribute_set_len(handle: iox2_attribute_set_h_ref /// * The `index` < [`iox2_attribute_set_len()`]. #[no_mangle] pub unsafe extern "C" fn iox2_attribute_set_at( - handle: iox2_attribute_set_h_ref, + handle: iox2_attribute_set_ptr, index: usize, ) -> iox2_attribute_h_ref { debug_assert!(!handle.is_null()); debug_assert!(index < iox2_attribute_set_len(handle)); - let attribute_set = (*handle).underlying_type(); - (&attribute_set[index] as *const Attribute).cast() + (&(*handle)[index] as *const Attribute).cast() +} + +/// Returns the number of values stored under a specific key. If the key does not exist it +/// returns 0. +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * `key` must be non-zero and contain a null-terminated string +#[no_mangle] +pub unsafe extern "C" fn iox2_attribute_set_get_key_value_len( + handle: iox2_attribute_set_ptr, + key: *const c_char, +) -> usize { + debug_assert!(!handle.is_null()); + debug_assert!(!key.is_null()); + + let key = CStr::from_ptr(key); + let key = key.to_str(); + if key.is_err() { + return 0; + } + + (*handle).get_key_value_len(key.unwrap()) +} + +/// Returns a value of a key at a specific index. The index enumerates the values of the key +/// if the key has multiple values. The values are always stored at the same position during +/// the lifetime of the service but they can change when the process is recreated by another +/// process when the system restarts. +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * `key` must be non-zero and contain a null-terminated string +/// * `buffer` must point to a valid memory location +/// * `buffer_len` must define the length of the memory pointed by `buffer` +#[no_mangle] +pub unsafe extern "C" fn iox2_attribute_set_get_key_value_at( + handle: iox2_attribute_set_ptr, + key: *const c_char, + index: usize, + buffer: *mut c_char, + buffer_len: usize, + has_value: *mut bool, +) { + debug_assert!(!handle.is_null()); + debug_assert!(!key.is_null()); + debug_assert!(!buffer.is_null()); + debug_assert!(0 < buffer_len); + + *has_value = false; + let key = CStr::from_ptr(key).to_str(); + if key.is_err() { + return; + } + + if let Some(v) = (*handle).get_key_value_at(key.unwrap(), index) { + if let Ok(value) = CString::new(v) { + core::ptr::copy_nonoverlapping( + value.as_ptr(), + buffer, + buffer_len.min(value.count_bytes() + 1 /* null terminator */), + ); + *has_value = true; + } + } } /// Calls the provided callback for every value that is owned by the provided key. @@ -80,14 +271,13 @@ pub unsafe extern "C" fn iox2_attribute_set_at( /// * The `callback` must point to a function with the required signature. #[no_mangle] pub unsafe extern "C" fn iox2_attribute_set_get_key_values( - handle: iox2_attribute_set_h_ref, + handle: iox2_attribute_set_ptr, key: *const c_char, callback: iox2_attribute_set_get_callback, callback_ctx: iox2_callback_context, ) { debug_assert!(!handle.is_null()); - let attribute_set = (*handle).underlying_type(); let key = CStr::from_ptr(key); let c_str = key.to_str(); if c_str.is_err() { @@ -96,7 +286,7 @@ pub unsafe extern "C" fn iox2_attribute_set_get_key_values( let c_str = c_str.unwrap(); - attribute_set.get_key_values(c_str, |value| { + (*handle).get_key_values(c_str, |value| { if let Ok(value) = CString::new(value) { callback(value.as_ptr(), callback_ctx).into() } else { diff --git a/iceoryx2-ffi/ffi/src/api/attribute_specifier.rs b/iceoryx2-ffi/ffi/src/api/attribute_specifier.rs index 6b14402b8..a0700a08c 100644 --- a/iceoryx2-ffi/ffi/src/api/attribute_specifier.rs +++ b/iceoryx2-ffi/ffi/src/api/attribute_specifier.rs @@ -24,7 +24,7 @@ use core::{ mem::ManuallyDrop, }; -use super::iox2_attribute_set_h_ref; +use super::iox2_attribute_set_ptr; // BEGIN type definition @@ -180,19 +180,19 @@ pub unsafe extern "C" fn iox2_attribute_specifier_define( )); } -/// Returnes a [`iox2_attribute_set_h_ref`] to the underlying attribute set. +/// Returnes a [`iox2_attribute_set_ptr`] to the underlying attribute set. /// /// # Safety /// /// * The `handle` must point to an initialized [`iox2_attribute_specifier_h`]. -/// * The `handle` must live at least as long as the returned [`iox2_attribute_set_h_ref`]. +/// * The `handle` must live at least as long as the returned [`iox2_attribute_set_ptr`]. #[no_mangle] pub unsafe extern "C" fn iox2_attribute_specifier_attributes( handle: iox2_attribute_specifier_h_ref, -) -> iox2_attribute_set_h_ref { +) -> iox2_attribute_set_ptr { debug_assert!(!handle.is_null()); let attribute_specifier_struct = &mut *handle.as_type(); - (attribute_specifier_struct.value.as_ref().0.attributes() as *const AttributeSet).cast() + attribute_specifier_struct.value.as_ref().0.attributes() } // END C API diff --git a/iceoryx2-ffi/ffi/src/api/attribute_verifier.rs b/iceoryx2-ffi/ffi/src/api/attribute_verifier.rs index d3eeb8165..7c9f12980 100644 --- a/iceoryx2-ffi/ffi/src/api/attribute_verifier.rs +++ b/iceoryx2-ffi/ffi/src/api/attribute_verifier.rs @@ -27,7 +27,7 @@ use core::{ extern crate alloc; use alloc::ffi::CString; -use super::iox2_attribute_set_h_ref; +use super::iox2_attribute_set_ptr; // BEGIN type definition @@ -208,23 +208,23 @@ pub unsafe extern "C" fn iox2_attribute_verifier_require_key( )); } -/// Returnes a [`iox2_attribute_set_h_ref`] to the underlying attribute set. +/// Returnes a [`iox2_attribute_set_ptr`] to the underlying attribute set. /// /// # Safety /// /// * The `handle` must point to an initialized [`iox2_attribute_verifier_h`]. -/// * The `handle` must live at least as long as the returned [`iox2_attribute_set_h_ref`]. +/// * The `handle` must live at least as long as the returned [`iox2_attribute_set_ptr`]. #[no_mangle] pub unsafe extern "C" fn iox2_attribute_verifier_attributes( handle: iox2_attribute_verifier_h_ref, -) -> iox2_attribute_set_h_ref { +) -> iox2_attribute_set_ptr { debug_assert!(!handle.is_null()); let attribute_verifier_struct = &mut *handle.as_type(); - (attribute_verifier_struct.value.as_ref().0.attributes() as *const AttributeSet).cast() + attribute_verifier_struct.value.as_ref().0.attributes() } -/// Verifies if the [`iox2_attribute_set_h_ref`] contains all required keys and key-value pairs. +/// Verifies if the [`iox2_attribute_set_ptr`] contains all required keys and key-value pairs. /// /// # Safety /// @@ -235,7 +235,7 @@ pub unsafe extern "C" fn iox2_attribute_verifier_attributes( #[no_mangle] pub unsafe extern "C" fn iox2_attribute_verifier_verify_requirements( handle: iox2_attribute_verifier_h_ref, - rhs: iox2_attribute_set_h_ref, + rhs: iox2_attribute_set_ptr, incompatible_key_buffer: *mut c_char, incompatible_key_buffer_len: usize, ) -> bool { @@ -245,7 +245,7 @@ pub unsafe extern "C" fn iox2_attribute_verifier_verify_requirements( let attribute_verifier_struct = &mut *handle.as_type(); let attribute_verifier = &attribute_verifier_struct.value.as_ref().0; - match attribute_verifier.verify_requirements((*rhs).underlying_type()) { + match attribute_verifier.verify_requirements(&*rhs) { Ok(()) => true, Err(incompatible_key) => { if let Ok(incompatible_key) = CString::new(incompatible_key) { diff --git a/iceoryx2-ffi/ffi/src/api/node.rs b/iceoryx2-ffi/ffi/src/api/node.rs index a562b8933..c1ebf7afc 100644 --- a/iceoryx2-ffi/ffi/src/api/node.rs +++ b/iceoryx2-ffi/ffi/src/api/node.rs @@ -410,7 +410,7 @@ pub unsafe extern "C" fn iox2_dead_node_remove_stale_resources( } } -fn iox2_node_list_impl( +pub(crate) fn iox2_node_list_impl( node_state: &NodeState, callback: iox2_node_list_callback, callback_ctx: iox2_callback_context, diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_event.rs b/iceoryx2-ffi/ffi/src/api/port_factory_event.rs index f8bc66d23..77bd0d6d6 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_event.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_event.rs @@ -15,18 +15,23 @@ use crate::api::{ iox2_port_factory_listener_builder_h, iox2_port_factory_listener_builder_t, iox2_port_factory_notifier_builder_h, iox2_port_factory_notifier_builder_t, - iox2_service_name_ptr, iox2_service_type_e, AssertNonNullHandle, HandleToType, + iox2_service_name_ptr, iox2_service_type_e, AssertNonNullHandle, HandleToType, IntoCInt, PortFactoryListenerBuilderUnion, PortFactoryNotifierBuilderUnion, }; +use crate::{iox2_node_list_impl, IOX2_OK}; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::{event::PortFactory as PortFactoryEvent, PortFactory}; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; +use core::ffi::{c_char, c_int}; use core::mem::ManuallyDrop; -use super::{iox2_attribute_set_h_ref, iox2_static_config_event_t}; +use super::{ + iox2_attribute_set_ptr, iox2_callback_context, iox2_node_list_callback, + iox2_static_config_event_t, +}; // BEGIN types definition @@ -278,19 +283,153 @@ pub unsafe extern "C" fn iox2_port_factory_event_listener_builder( #[no_mangle] pub unsafe extern "C" fn iox2_port_factory_event_attributes( port_factory_handle: iox2_port_factory_event_h_ref, -) -> iox2_attribute_set_h_ref { +) -> iox2_attribute_set_ptr { use iceoryx2::prelude::PortFactory; port_factory_handle.assert_non_null(); let port_factory = &mut *port_factory_handle.as_type(); match port_factory.service_type { - iox2_service_type_e::IPC => { - (port_factory.value.as_ref().ipc.attributes() as *const AttributeSet).cast() - } - iox2_service_type_e::LOCAL => { - (port_factory.value.as_ref().local.attributes() as *const AttributeSet).cast() - } + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.attributes(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.attributes(), + } +} + +/// Returns how many listener ports are currently connected. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_event_open`](crate::iox2_service_builder_event_open) or +/// [`iox2_service_builder_event_open_or_create`](crate::iox2_service_builder_event_open_or_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_event_dynamic_config_number_of_listeners( + handle: iox2_port_factory_event_h_ref, +) -> usize { + use iceoryx2::prelude::PortFactory; + + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .dynamic_config() + .number_of_listeners(), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .dynamic_config() + .number_of_listeners(), + } +} + +/// Returns how many notifier ports are currently connected. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_event_open`](crate::iox2_service_builder_event_open) or +/// [`iox2_service_builder_event_open_or_create`](crate::iox2_service_builder_event_open_or_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_event_dynamic_config_number_of_notifiers( + handle: iox2_port_factory_event_h_ref, +) -> usize { + use iceoryx2::prelude::PortFactory; + + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .dynamic_config() + .number_of_notifiers(), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .dynamic_config() + .number_of_notifiers(), + } +} + +/// Stores the service id in the provided buffer +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_event_open`](crate::iox2_service_builder_event_open) or +/// [`iox2_service_builder_event_open_or_create`](crate::iox2_service_builder_event_open_or_create)! +/// * `buffer` must be non-zero and point to a valid memory location +/// * `buffer_len` must define the actual size of the memory location `buffer` is pointing to +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_event_service_id( + handle: iox2_port_factory_event_h_ref, + buffer: *mut c_char, + buffer_len: usize, +) { + use iceoryx2::prelude::PortFactory; + + debug_assert!(!buffer.is_null()); + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + let service_id = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.service_id(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.service_id(), + }; + + let len = buffer_len.min(service_id.as_str().len()); + core::ptr::copy_nonoverlapping(service_id.as_str().as_ptr(), buffer.cast(), len); + buffer.add(len).write(0); +} + +/// Calls the callback repeatedly with an [`iox2_node_state_e`](crate::api::iox2_node_state_e), +/// [`iox2_node_id_ptr`](crate::api::iox2_node_id_ptr), +/// [´iox2_node_name_ptr´](crate::api::iox2_node_name_ptr) and +/// [`iox2_config_ptr`](crate::api::iox2_config_ptr) for all [`Node`](iceoryx2::node::Node)s that +/// have opened the service. +/// +/// Returns IOX2_OK on success, an +/// [`iox2_node_list_failure_e`](crate::api::iox2_node_list_failure_e) otherwise. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +/// * `callback` - A valid callback with [`iox2_node_list_callback`] signature +/// * `callback_ctx` - An optional callback context [`iox2_callback_context`] to e.g. store information across callback iterations +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_event_nodes( + handle: iox2_port_factory_event_h_ref, + callback: iox2_node_list_callback, + callback_ctx: iox2_callback_context, +) -> c_int { + use iceoryx2::prelude::PortFactory; + + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + + let list_result = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .nodes(|node_state| iox2_node_list_impl(&node_state, callback, callback_ctx)), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .nodes(|node_state| iox2_node_list_impl(&node_state, callback, callback_ctx)), + }; + + match list_result { + Ok(_) => IOX2_OK, + Err(e) => e.into_c_int(), } } diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs index 001859e97..147122f19 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs @@ -12,11 +12,15 @@ #![allow(non_camel_case_types)] -use crate::api::{ - iox2_port_factory_publisher_builder_h, iox2_port_factory_publisher_builder_t, - iox2_port_factory_subscriber_builder_h, iox2_port_factory_subscriber_builder_t, - iox2_service_type_e, iox2_static_config_publish_subscribe_t, AssertNonNullHandle, HandleToType, - PayloadFfi, PortFactoryPublisherBuilderUnion, PortFactorySubscriberBuilderUnion, UserHeaderFfi, +use crate::{ + api::{ + iox2_port_factory_publisher_builder_h, iox2_port_factory_publisher_builder_t, + iox2_port_factory_subscriber_builder_h, iox2_port_factory_subscriber_builder_t, + iox2_service_type_e, iox2_static_config_publish_subscribe_t, AssertNonNullHandle, + HandleToType, IntoCInt, PayloadFfi, PortFactoryPublisherBuilderUnion, + PortFactorySubscriberBuilderUnion, UserHeaderFfi, + }, + iox2_node_list_impl, IOX2_OK, }; use iceoryx2::prelude::*; @@ -24,9 +28,14 @@ use iceoryx2::service::port_factory::publish_subscribe::PortFactory; use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; -use core::mem::ManuallyDrop; +use core::{ + ffi::{c_char, c_int}, + mem::ManuallyDrop, +}; -use super::iox2_attribute_set_h_ref; +use super::{ + iox2_attribute_set_ptr, iox2_callback_context, iox2_node_list_callback, iox2_service_name_ptr, +}; // BEGIN types definition @@ -235,19 +244,15 @@ pub unsafe extern "C" fn iox2_port_factory_pub_sub_subscriber_builder( #[no_mangle] pub unsafe extern "C" fn iox2_port_factory_pub_sub_attributes( port_factory_handle: iox2_port_factory_pub_sub_h_ref, -) -> iox2_attribute_set_h_ref { +) -> iox2_attribute_set_ptr { use iceoryx2::prelude::PortFactory; port_factory_handle.assert_non_null(); let port_factory = &mut *port_factory_handle.as_type(); match port_factory.service_type { - iox2_service_type_e::IPC => { - (port_factory.value.as_ref().ipc.attributes() as *const AttributeSet).cast() - } - iox2_service_type_e::LOCAL => { - (port_factory.value.as_ref().local.attributes() as *const AttributeSet).cast() - } + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.attributes(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.attributes(), } } @@ -277,6 +282,167 @@ pub unsafe extern "C" fn iox2_port_factory_pub_sub_static_config( *static_config = config.into(); } +/// Returns how many publisher ports are currently connected. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_dynamic_config_number_of_publishers( + handle: iox2_port_factory_pub_sub_h_ref, +) -> usize { + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + + use iceoryx2::prelude::PortFactory; + match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .dynamic_config() + .number_of_publishers(), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .dynamic_config() + .number_of_publishers(), + } +} + +/// Calls the callback repeatedly with an [`iox2_node_state_e`](crate::api::iox2_node_state_e), +/// [`iox2_node_id_ptr`](crate::api::iox2_node_id_ptr), +/// [´iox2_node_name_ptr´](crate::api::iox2_node_name_ptr) and +/// [`iox2_config_ptr`](crate::api::iox2_config_ptr) for all +/// [`Node`](iceoryx2::node::Node)s that +/// have opened the service. +/// +/// Returns IOX2_OK on success, an +/// [`iox2_node_list_failure_e`](crate::api::iox2_node_list_failure_e) otherwise. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +/// * `callback` - A valid callback with [`iox2_node_list_callback`} signature +/// * `callback_ctx` - An optional callback context [`iox2_callback_context`} to e.g. store information across callback iterations +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_nodes( + handle: iox2_port_factory_pub_sub_h_ref, + callback: iox2_node_list_callback, + callback_ctx: iox2_callback_context, +) -> c_int { + use iceoryx2::prelude::PortFactory; + + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + + let list_result = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .nodes(|node_state| iox2_node_list_impl(&node_state, callback, callback_ctx)), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .nodes(|node_state| iox2_node_list_impl(&node_state, callback, callback_ctx)), + }; + + match list_result { + Ok(_) => IOX2_OK, + Err(e) => e.into_c_int(), + } +} + +/// Returns the [`iox2_service_name_ptr`], an immutable pointer to the service name. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_service_name( + handle: iox2_port_factory_pub_sub_h_ref, +) -> iox2_service_name_ptr { + use iceoryx2::prelude::PortFactory; + + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + + match port_factory.service_type { + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.name(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.name(), + } +} + +/// Stores the service id in the provided buffer +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +/// * `buffer` must be non-zero and point to a valid memory location +/// * `buffer_len` must define the actual size of the memory location `buffer` is pointing to +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_service_id( + handle: iox2_port_factory_pub_sub_h_ref, + buffer: *mut c_char, + buffer_len: usize, +) { + use iceoryx2::prelude::PortFactory; + + debug_assert!(!buffer.is_null()); + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + let service_id = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.service_id(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.service_id(), + }; + + let len = buffer_len.min(service_id.as_str().len()); + core::ptr::copy_nonoverlapping(service_id.as_str().as_ptr(), buffer.cast(), len); + buffer.add(len).write(0); +} + +/// Returns how many subscriber ports are currently connected. +/// +/// # Safety +/// +/// * The `handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_dynamic_config_number_of_subscribers( + handle: iox2_port_factory_pub_sub_h_ref, +) -> usize { + handle.assert_non_null(); + + let port_factory = &mut *handle.as_type(); + + use iceoryx2::prelude::PortFactory; + match port_factory.service_type { + iox2_service_type_e::IPC => port_factory + .value + .as_ref() + .ipc + .dynamic_config() + .number_of_subscribers(), + iox2_service_type_e::LOCAL => port_factory + .value + .as_ref() + .local + .dynamic_config() + .number_of_subscribers(), + } +} + /// This function needs to be called to destroy the port factory! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/service.rs b/iceoryx2-ffi/ffi/src/api/service.rs index e8d6212a7..89bfa94fb 100644 --- a/iceoryx2-ffi/ffi/src/api/service.rs +++ b/iceoryx2-ffi/ffi/src/api/service.rs @@ -228,19 +228,55 @@ pub unsafe extern "C" fn iox2_service_does_exist( /// * The `service_name` must be valid and non-null /// * The `config` must be valid and non-null /// * The `service_details` must be valid and non-null +/// * The `does_exist` must be valid and non-null #[no_mangle] pub unsafe extern "C" fn iox2_service_details( - _service_type: iox2_service_type_e, + service_type: iox2_service_type_e, service_name: iox2_service_name_ptr, config: iox2_config_ptr, - _messaging_pattern: iox2_messaging_pattern_e, + messaging_pattern: iox2_messaging_pattern_e, service_details: *mut iox2_static_config_t, + does_exist: *mut bool, ) -> c_int { debug_assert!(!service_name.is_null()); debug_assert!(!config.is_null()); debug_assert!(!service_details.is_null()); + debug_assert!(!does_exist.is_null()); + + let config = &*config; + let service_name = &*service_name; + let messaging_pattern = messaging_pattern.into(); - todo!() + match service_type { + iox2_service_type_e::IPC => { + match ipc::Service::details(service_name, config, messaging_pattern) { + Ok(None) => { + does_exist.write(false); + IOX2_OK + } + Err(e) => e.into_c_int(), + Ok(Some(v)) => { + service_details.write((&v.static_details).into()); + does_exist.write(true); + IOX2_OK + } + } + } + iox2_service_type_e::LOCAL => { + match local::Service::details(service_name, config, messaging_pattern) { + Ok(None) => { + does_exist.write(false); + IOX2_OK + } + Err(e) => e.into_c_int(), + Ok(Some(v)) => { + service_details.write((&v.static_details).into()); + does_exist.write(true); + IOX2_OK + } + } + } + } } fn list_callback( diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs index 690a8af8c..21ad63c5c 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs @@ -532,6 +532,90 @@ pub unsafe extern "C" fn iox2_service_builder_event_set_max_notifiers( } } +/// Sets the max nodes for the builder +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// * `value` - The value to set the max notifiers to +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_set_max_nodes( + service_builder_handle: iox2_service_builder_event_h_ref, + value: c_size_t, +) { + service_builder_handle.assert_non_null(); + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + + match service_builder_struct.service_type { + iox2_service_type_e::IPC => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder.max_nodes(value), + )); + } + iox2_service_type_e::LOCAL => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_local_event( + service_builder.max_nodes(value), + )); + } + } +} + +/// Sets the max event id value for the builder +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// * `value` - The value to set the max notifiers to +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_set_event_id_max_value( + service_builder_handle: iox2_service_builder_event_h_ref, + value: c_size_t, +) { + service_builder_handle.assert_non_null(); + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + + match service_builder_struct.service_type { + iox2_service_type_e::IPC => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder.event_id_max_value(value), + )); + } + iox2_service_type_e::LOCAL => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_local_event( + service_builder.event_id_max_value(value), + )); + } + } +} + /// Sets the max listeners for the builder /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/static_config.rs b/iceoryx2-ffi/ffi/src/api/static_config.rs index 86d7ee184..cb9586d86 100644 --- a/iceoryx2-ffi/ffi/src/api/static_config.rs +++ b/iceoryx2-ffi/ffi/src/api/static_config.rs @@ -23,6 +23,8 @@ use crate::{ IOX2_SERVICE_ID_LENGTH, IOX2_SERVICE_NAME_LENGTH, }; +use super::{iox2_attribute_set_h, iox2_attribute_set_new_clone}; + #[derive(Clone, Copy)] #[repr(C)] pub union iox2_static_config_details_t { @@ -37,12 +39,26 @@ pub struct iox2_static_config_t { pub name: [c_char; IOX2_SERVICE_NAME_LENGTH], pub messaging_pattern: iox2_messaging_pattern_e, pub details: iox2_static_config_details_t, + pub attributes: iox2_attribute_set_h, } impl From<&StaticConfig> for iox2_static_config_t { fn from(value: &StaticConfig) -> Self { + let mut attribute_handle_ptr: iox2_attribute_set_h = core::ptr::null_mut(); + + unsafe { + iox2_attribute_set_new_clone( + core::ptr::null_mut(), + value.attributes(), + &mut attribute_handle_ptr, + ) + }; + debug_assert!(!attribute_handle_ptr.is_null()); + Self { id: core::array::from_fn(|n| { + debug_assert!(value.service_id().as_str().len() + 1 < IOX2_SERVICE_ID_LENGTH); + let raw_service_id = value.service_id().as_str().as_bytes(); if n < raw_service_id.len() { raw_service_id[n] as _ @@ -59,6 +75,7 @@ impl From<&StaticConfig> for iox2_static_config_t { 0 } }), + attributes: attribute_handle_ptr, messaging_pattern: value.messaging_pattern().into(), details: { match value.messaging_pattern() { diff --git a/iceoryx2/src/service/dynamic_config/publish_subscribe.rs b/iceoryx2/src/service/dynamic_config/publish_subscribe.rs index 8aadf60a6..f2fec7ca5 100644 --- a/iceoryx2/src/service/dynamic_config/publish_subscribe.rs +++ b/iceoryx2/src/service/dynamic_config/publish_subscribe.rs @@ -69,7 +69,8 @@ pub struct SubscriberDetails { pub buffer_size: usize, } -/// The dynamic configuration of an [`crate::service::messaging_pattern::MessagingPattern::Event`] +/// The dynamic configuration of an +/// [`crate::service::messaging_pattern::MessagingPattern::PublishSubscribe`] /// based service. Contains dynamic parameters like the connected endpoints etc.. #[repr(C)] #[derive(Debug)]