diff --git a/rcl/include/rcl/graph.h b/rcl/include/rcl/graph.h
index 8848c95a9e..025d163f0b 100644
--- a/rcl/include/rcl/graph.h
+++ b/rcl/include/rcl/graph.h
@@ -525,22 +525,22 @@ rcl_count_subscribers(
size_t * count);
/// Returns a list of all publishers to a topic.
-/// Each element in the list will contain the publisher's name and its respective qos profile.
+/// Each element in the list will contain the node name, node namespace, topic type,
+/// gid and the qos profile of the publisher.
/**
* The `node` parameter must point to a valid node.
*
* The `topic_name` parameter must not be `NULL`.
*
- * The `publishers` parameter must point to a valid struct of type rmw_participants_t.
- * The `count` field inside the struct must be set to 0
- * The `participants` field inside the struct must be set to null.
- * The `publishers` parameter is the output for this function and will be set.
+ * The `no_mangle` parameter determines if the provided topic_name should be
+ * expanded to its fully qualified name.
*
- * The topic name is not automatically remapped by this function.
- * If there is a publisher created with topic name `foo` and remap rule `foo:=bar` then calling
- * this with `topic_name` set to `bar` will return a list with 1 publisher, and with `topic_name` set to `foo`
- * will return a list with 0 publishers.
- * /sa rcl_remap_topic_name()
+ * It is the responsibility of the caller to ensure that `publishers_info` parameter points
+ * to a valid struct of type rmw_topic_info_array_t. The `count` field inside the struct
+ * must be set to 0 and the `info_array` field inside the struct must be set to null.
+ *
+ * The `allocator` will be used to allocate memory to the `info_array` member
+ * inside of `publishers_info`.
*
*
* Attribute | Adherence
@@ -552,8 +552,11 @@ rcl_count_subscribers(
* [1] implementation may need to protect the data structure with a lock
*
* \param[in] node the handle to the node being used to query the ROS graph
+ * \param[in] allocator allocator to be used when allocating space for
+ * the array inside publishers_info
* \param[in] topic_name the name of the topic in question
- * \param[out] publishers a struct representing a list of publishers with their qos profile.
+ * \param[in] no_mangle if true, the topic_name will be expanded to its fully qualified name.
+ * \param[out] publishers_info a struct representing a list of publisher information.
* \return `RCL_RET_OK` if the query was successful, or
* \return `RCL_RET_NODE_INVALID` if the node is invalid, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
@@ -562,29 +565,32 @@ rcl_count_subscribers(
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
-rcl_get_qos_for_publishers(
+rcl_get_publishers_info_by_topic(
const rcl_node_t * node,
+ rcutils_allocator_t * allocator,
const char * topic_name,
- rmw_participants_t * publishers);
+ bool no_mangle,
+ rmw_topic_info_array_t * publishers_info);
+
-/// Returns a list of all subscribers to a topic.
-/// Each element in the list will contain the subscriber's name and its respective qos profile.
+/// Returns a list of all subscriptions to a topic.
+/// Each element in the list will contain the node name, node namespace, topic type,
+/// gid and the qos profile of the subscription.
/**
* The `node` parameter must point to a valid node.
*
* The `topic_name` parameter must not be `NULL`.
*
- * The `subscriber` parameter must point to a valid struct of type rmw_participants_t.
- * The `count` field inside the struct must be set to 0
- * The `participants` field inside the struct must be set to null.
- * The `subscribers` parameter is the output for this function and will be set.
+ * The `no_mangle` parameter determines if the provided topic_name should be
+ * expanded to its fully qualified name.
*
- * The topic name is not automatically remapped by this function.
- * If there is a subscriber created with topic name `foo` and remap rule `foo:=bar` then calling
- * this with `topic_name` set to `bar` will return a list with 1 subscriber , and with `topic_name` set to `foo`
- * will return a list with 0 subscribers.
- * /sa rcl_remap_topic_name()
+ * It is the responsibility of the caller to ensure that `subscriptions_info` parameter points
+ * to a valid struct of type rmw_topic_info_array_t. The `count` field inside the struct
+ * must be set to 0 and the `info_array` field inside the struct must be set to null.
*
+ * The `allocator` will be used to allocate memory to the `info_array` member
+ * inside of `subscriptions_info`.
+
*
* Attribute | Adherence
* ------------------ | -------------
@@ -595,8 +601,11 @@ rcl_get_qos_for_publishers(
* [1] implementation may need to protect the data structure with a lock
*
* \param[in] node the handle to the node being used to query the ROS graph
+ * \param[in] allocator allocator to be used when allocating space for
+ * the array inside publishers_info
* \param[in] topic_name the name of the topic in question
- * \param[out] subscribers a struct representing a list of subscribers with their qos profile.
+ * \param[in] no_mangle if true, the topic_name will be expanded to its fully qualified name.
+ * \param[out] subscriptions_info a struct representing a list of subscriptions information.
* \return `RCL_RET_OK` if the query was successful, or
* \return `RCL_RET_NODE_INVALID` if the node is invalid, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
@@ -605,10 +614,12 @@ rcl_get_qos_for_publishers(
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
-rcl_get_qos_for_subscribers(
+rcl_get_subscriptions_info_by_topic(
const rcl_node_t * node,
+ rcutils_allocator_t * allocator,
const char * topic_name,
- rmw_participants_t * subscribers);
+ bool no_mangle,
+ rmw_topic_info_array_t * subscriptions_info);
/// Check if a service server is available for the given service client.
diff --git a/rcl/src/rcl/graph.c b/rcl/src/rcl/graph.c
index 331d780277..c539544e42 100644
--- a/rcl/src/rcl/graph.c
+++ b/rcl/src/rcl/graph.c
@@ -375,6 +375,73 @@ rcl_count_subscribers(
return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
}
+
+typedef rmw_ret_t (* get_topic_info_func)(
+ const rcl_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * topic_name,
+ bool no_mangle,
+ rmw_topic_info_array_t * info_array);
+
+rcl_ret_t
+__rcl_get_info_by_topic(
+ const rmw_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * topic_name,
+ bool no_mangle,
+ rmw_topic_info_array_t * info_array,
+ get_topic_info_func get_topic_info)
+{
+ if (!rcl_node_is_valid(node)) {
+ RCL_SET_ERROR_MSG("Invalid node provided.");
+ return RCL_RET_NODE_INVALID;
+ }
+ const rcl_node_options_t * node_options = rcl_node_get_options(node);
+ if (!node_options) {
+ RCL_SET_ERROR_MSG("Node options are invalid.");
+ return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so
+ }
+ RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
+ RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
+ RCL_CHECK_ARGUMENT_FOR_NULL(info_array, RCL_RET_INVALID_ARGUMENT);
+ if (info_array->info_array != NULL) {
+ RCL_SET_ERROR_MSG("The variable participants inside rmw_participants_t should be set to null. "
+ "It will be malloc'd where it gets populated.");
+ return RCL_RET_INVALID_ARGUMENT;
+ }
+ rmw_ret_t rmw_ret = get_topic_info(
+ rcl_node_get_rmw_handle(node),
+ allocator,
+ topic_name,
+ no_mangle,
+ info_array);
+ return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
+}
+
+rcl_ret_t
+rcl_get_publishers_info_by_topic(
+ const rcl_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * topic_name,
+ bool no_mangle,
+ rmw_topic_info_array_t * publishers_info)
+{
+ return __rcl_get_info_by_topic(node, allocator, topic_name, no_mangle, publishers_info,
+ rmw_get_publishers_info_by_topic);
+}
+
+rcl_ret_t
+rcl_get_subscriptions_info_by_topic(
+ const rcl_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * topic_name,
+ bool no_mangle,
+ rmw_topic_info_array_t * subscriptions_info)
+{
+ return __rcl_get_info_by_topic(node, allocator, topic_name, no_mangle, subscriptions_info,
+ rmw_get_subscriptions_info_by_topic);
+}
+
rcl_ret_t
rcl_service_server_is_available(
const rcl_node_t * node,
diff --git a/rcl/test/rcl/test_graph.cpp b/rcl/test/rcl/test_graph.cpp
index 28f7cdae78..11b58b0d60 100644
--- a/rcl/test/rcl/test_graph.cpp
+++ b/rcl/test/rcl/test_graph.cpp
@@ -63,6 +63,9 @@ class CLASSNAME (TestGraphFixture, RMW_IMPLEMENTATION) : public ::testing::Test
rcl_wait_set_t * wait_set_ptr;
const char * test_graph_node_name = "test_graph_node";
+ rmw_topic_info_array_t * topic_info_array;
+ const char * topic_name = "valid_topic_name";
+
void SetUp()
{
rcl_ret_t ret;
@@ -101,6 +104,12 @@ class CLASSNAME (TestGraphFixture, RMW_IMPLEMENTATION) : public ::testing::Test
ret = rcl_wait_set_init(
this->wait_set_ptr, 0, 1, 0, 0, 0, 0, this->context_ptr, rcl_get_default_allocator());
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str;
+
+ rmw_topic_info_array_t valid_topic_info_array = {
+ 0, /*count*/
+ nullptr /*info_array*/
+ };
+ this->topic_info_array = &valid_topic_info_array;
}
void TearDown()
@@ -1321,3 +1330,172 @@ TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), test_rcl_service_server_
wait_for_service_state_to_change(false, is_available);
ASSERT_FALSE(is_available);
}
+
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_null_node)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_publishers_info_by_topic(nullptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_NODE_INVALID, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_null_node)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_subscriptions_info_by_topic(nullptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_NODE_INVALID, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_invalid_node)
+{
+ // this->old_node_ptr is a pointer to an invalid node.
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_publishers_info_by_topic(this->old_node_ptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_NODE_INVALID, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_invalid_node)
+{
+ // this->old_node_ptr is a pointer to an invalid node.
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_subscriptions_info_by_topic(this->old_node_ptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_NODE_INVALID, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_null_allocator)
+{
+ auto ret = rcl_get_publishers_info_by_topic(this->node_ptr, nullptr, this->topic_name, false,
+ this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_null_allocator)
+{
+ auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr, nullptr, this->topic_name, false,
+ this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_null_topic)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_publishers_info_by_topic(this->node_ptr,
+ &allocator, nullptr, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_null_topic)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr,
+ &allocator, nullptr, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_null_participants)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_publishers_info_by_topic(this->node_ptr,
+ &allocator, this->topic_name, false, nullptr);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_null_participants)
+{
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr,
+ &allocator, this->topic_name, false, nullptr);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_publishers_info_by_topic_invalid_participants)
+{
+ // this participant is invalid as the pointer "participants" inside is expected to be null.
+ this->topic_info_array->info_array =
+ reinterpret_cast(malloc(sizeof(rmw_topic_info_t *)));
+ OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({
+ free(this->topic_info_array->info_array);
+ });
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_publishers_info_by_topic(this->node_ptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}
+
+/*
+ * This does not test content of the response.
+ * It only tests if the return code is the one expected.
+ */
+TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION),
+ test_rcl_get_subscriptions_info_by_topic_invalid_participants)
+{
+ // this participant is invalid as the pointer "participants" inside is expected to be null.
+ this->topic_info_array->info_array =
+ reinterpret_cast(malloc(sizeof(rmw_topic_info_t *)));
+ OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({
+ free(this->topic_info_array->info_array);
+ });
+ rcl_allocator_t allocator = rcl_get_default_allocator();
+ auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr,
+ &allocator, this->topic_name, false, this->topic_info_array);
+ EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
+}