diff --git a/rcl/include/rcl/node.h b/rcl/include/rcl/node.h
index 2f1999c..2f63105 100644
--- a/rcl/include/rcl/node.h
+++ b/rcl/include/rcl/node.h
@@ -352,6 +352,29 @@ RCL_WARN_UNUSED
const char *
rcl_node_get_namespace(const rcl_node_t * node);
+/// Return the fully qualified name of the node.
+/**
+ * This function returns the node's internal namespace and name combined string.
+ * This function can fail, and therefore return `NULL`, if:
+ * - node is `NULL`
+ * - node has not been initialized (the implementation is invalid)
+ *
+ *
+ * Attribute | Adherence
+ * ------------------ | -------------
+ * Allocates Memory | Yes
+ * Thread-Safe | No
+ * Uses Atomics | No
+ * Lock-Free | Yes
+ *
+ * \param[in] node pointer to the node
+ * \return fully qualified name string if successful, otherwise `NULL`
+ */
+RCL_PUBLIC
+RCL_WARN_UNUSED
+const char *
+rcl_node_get_fully_qualified_name(const rcl_node_t * node);
+
/// Return the rcl node options.
/**
* This function returns the node's internal options struct.
diff --git a/rcl/src/rcl/node.c b/rcl/src/rcl/node.c
index df7445b..c969dda 100644
--- a/rcl/src/rcl/node.c
+++ b/rcl/src/rcl/node.c
@@ -603,6 +603,31 @@ rcl_node_get_namespace(const rcl_node_t * node)
return node->impl->rmw_node_handle->namespace_;
}
+const char *
+rcl_node_get_fully_qualified_name(const rcl_node_t * node)
+{
+ if (!rcl_node_is_valid_except_context(node)) {
+ return NULL; // error already set
+ }
+
+ const char * name = rcl_node_get_name(node);
+ const char * ns = rcl_node_get_namespace(node);
+ char * fq_name = NULL;
+
+ if ('/' == ns[strlen(ns) - 1]) {
+ fq_name = (char *)calloc(strlen(ns) + strlen(name), sizeof(char));
+ strcpy(fq_name, ns);
+ strcat(fq_name, name);
+ }
+ else {
+ fq_name = (char *)calloc(strlen(ns) + strlen(name) + 1, sizeof(char));
+ strcpy(fq_name, ns);
+ strcat(fq_name, "/");
+ strcat(fq_name, name);
+ }
+ return fq_name;
+}
+
const rcl_node_options_t *
rcl_node_get_options(const rcl_node_t * node)
{
diff --git a/rcl/test/rcl/test_node.cpp b/rcl/test/rcl/test_node.cpp
index 0a5d2f9..e3bcc4c 100644
--- a/rcl/test/rcl/test_node.cpp
+++ b/rcl/test/rcl/test_node.cpp
@@ -16,6 +16,7 @@
#include
#include
+#include
#include "rcl/rcl.h"
#include "rcl/node.h"
@@ -99,6 +100,7 @@ TEST_F(CLASSNAME(TestNodeFixture, RMW_IMPLEMENTATION), test_rcl_node_accessors)
rcl_node_t invalid_node = rcl_get_zero_initialized_node();
const char * name = "test_rcl_node_accessors_node";
const char * namespace_ = "/ns";
+ const char * fq_name = "/ns/test_rcl_node_accessors_node";
rcl_node_options_t default_options = rcl_node_get_default_options();
default_options.domain_id = 42; // Set the domain id to something explicit.
ret = rcl_node_init(&invalid_node, name, namespace_, &invalid_context, &default_options);
@@ -204,6 +206,27 @@ TEST_F(CLASSNAME(TestNodeFixture, RMW_IMPLEMENTATION), test_rcl_node_accessors)
if (actual_node_namespace) {
EXPECT_EQ(std::string(namespace_), std::string(actual_node_namespace));
}
+ // Test rcl_node_get_fully_qualified_name().
+ const char * actual_fq_node_name;
+ actual_fq_node_name = rcl_node_get_fully_qualified_name(nullptr);
+ EXPECT_EQ(nullptr, actual_fq_node_name);
+ rcl_reset_error();
+ actual_fq_node_name = rcl_node_get_fully_qualified_name(&zero_node);
+ EXPECT_EQ(nullptr, actual_fq_node_name);
+ rcl_reset_error();
+ actual_fq_node_name = rcl_node_get_fully_qualified_name(&invalid_node);
+ EXPECT_TRUE(actual_fq_node_name ? true : false);
+ if (actual_fq_node_name) {
+ EXPECT_STREQ(fq_name, actual_fq_node_name);
+ free((char *)actual_fq_node_name);
+ }
+ rcl_reset_error();
+ actual_fq_node_name = rcl_node_get_fully_qualified_name(&node);
+ EXPECT_TRUE(actual_fq_node_name ? true : false);
+ if (actual_fq_node_name) {
+ EXPECT_EQ(std::string(fq_name), std::string(actual_fq_node_name));
+ free((char *)actual_fq_node_name);
+ }
// Test rcl_node_get_logger_name().
const char * actual_node_logger_name;
actual_node_logger_name = rcl_node_get_logger_name(nullptr);