From eb571a5a2768f334901d2c115f370896caeb4c0a Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Wed, 14 Jan 2026 20:25:40 +0000 Subject: [PATCH 01/14] updating ROS2 publisher sink to accept NodeInterfaces Template as opposed to splitting constructors to accept lifecycle vs node --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 29 ++++++++++++------- .../src/sinks/ros2_publisher_sink.cpp | 12 -------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index c1c4138..3350842 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -6,21 +6,25 @@ #include "data_tamer_msgs/msg/snapshot.hpp" #include #include -#include +#include +#include namespace DataTamer { +using PublisherNodeInterfaces = + rclcpp::node_interfaces::NodeInterfaces; + class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(std::shared_ptr node, const std::string& topic_prefix); - - ROS2PublisherSink(std::shared_ptr node, - const std::string& topic_prefix); + ROS2PublisherSink(PublisherNodeInterfaces& interfaces, const std::string& topic_prefix) + : interfaces_(interfaces) + { + create_publishers(topic_prefix); + } - template - void create_publishers(NodeT& node, const std::string& topic_prefix) + void create_publishers(const std::string& topic_prefix) { rclcpp::QoS schemas_qos{ rclcpp::KeepAll() }; schemas_qos.reliable(); @@ -28,10 +32,10 @@ class ROS2PublisherSink : public DataSinkBase const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; - schema_publisher_ = node->template create_publisher( - topic_prefix + "/schemas", schemas_qos); - data_publisher_ = node->template create_publisher( - topic_prefix + "/data", data_qos); + schema_publisher_ = rclcpp::create_publisher( + interfaces_, topic_prefix + "/schemas", schemas_qos); + data_publisher_ = rclcpp::create_publisher( + interfaces_, topic_prefix + "/data", data_qos); } void addChannel(const std::string& name, const Schema& schema) override; @@ -47,6 +51,9 @@ class ROS2PublisherSink : public DataSinkBase bool schema_changed_ = true; data_tamer_msgs::msg::Snapshot data_msg_; + + // ---- Stored node façade ---- + PublisherNodeInterfaces& interfaces_; }; } // namespace DataTamer diff --git a/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp b/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp index b49c70b..92eaa32 100644 --- a/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp +++ b/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp @@ -2,18 +2,6 @@ namespace DataTamer { -ROS2PublisherSink::ROS2PublisherSink(std::shared_ptr node, - const std::string& topic_prefix) -{ - create_publishers(node, topic_prefix); -} - -ROS2PublisherSink::ROS2PublisherSink( - std::shared_ptr node, - const std::string& topic_prefix) -{ - create_publishers(node, topic_prefix); -} void ROS2PublisherSink::addChannel(const std::string& channel_name, const Schema& schema) { From c6c4c83760a934394e74fdd41d66ca9fca47b197 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Thu, 15 Jan 2026 14:42:18 +0000 Subject: [PATCH 02/14] pass by value and not reference for node interface --- .../include/data_tamer/sinks/ros2_publisher_sink.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 3350842..6b5c22f 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -18,7 +18,7 @@ using PublisherNodeInterfaces = class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(PublisherNodeInterfaces& interfaces, const std::string& topic_prefix) + ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) : interfaces_(interfaces) { create_publishers(topic_prefix); @@ -53,7 +53,7 @@ class ROS2PublisherSink : public DataSinkBase data_tamer_msgs::msg::Snapshot data_msg_; // ---- Stored node façade ---- - PublisherNodeInterfaces& interfaces_; + PublisherNodeInterfaces interfaces_; }; } // namespace DataTamer From 85dcf9b3a99bf95cf32eba62a1f6d4df195fb507 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Thu, 15 Jan 2026 15:46:17 +0000 Subject: [PATCH 03/14] dereferencing node in publisher example, and adding move in constructor to transfer Interface handles to Sink --- data_tamer_cpp/examples/ros2_publisher.cpp | 2 +- data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/examples/ros2_publisher.cpp b/data_tamer_cpp/examples/ros2_publisher.cpp index 5e88a59..cba36cb 100644 --- a/data_tamer_cpp/examples/ros2_publisher.cpp +++ b/data_tamer_cpp/examples/ros2_publisher.cpp @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) rclcpp::init(argc, argv); auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(node, "test"); + auto ros2_sink = std::make_shared(*node, "test"); ChannelsRegistry::Global().addDefaultSink(ros2_sink); // Create (or get) a channel using the global registry (singleton) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 6b5c22f..b31d992 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -19,7 +19,7 @@ class ROS2PublisherSink : public DataSinkBase { public: ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) - : interfaces_(interfaces) + : interfaces_(std::move(interfaces)) { create_publishers(topic_prefix); } From 8763fd9cd2356be095e59e19bb3490f09be800e2 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Sat, 24 Jan 2026 23:29:25 +0000 Subject: [PATCH 04/14] saving backwards compatibility progress --- .../include/data_tamer/sinks/ros2_publisher_sink.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index b31d992..a33b7ca 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -15,11 +15,18 @@ namespace DataTamer using PublisherNodeInterfaces = rclcpp::node_interfaces::NodeInterfaces; +// Concept: allow Node, LifecycleNode, or NodeInterface +template +concept NodeInterfaceType = std::same_as || + std::same_as || + std::same_as; + class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) - : interfaces_(std::move(interfaces)) + template + ROS2PublisherSink(NodeType interfaces, const std::string& topic_prefix) + : interfaces_(interfaces) { create_publishers(topic_prefix); } From 57df20f59c5e07dfcf1e4371b159202e8356a05d Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Sun, 25 Jan 2026 21:50:58 +0000 Subject: [PATCH 05/14] adding compatability for all nodelike types, and updating constructor to allow for backwards compatability with previous datatamer versions --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index a33b7ca..8e71503 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -5,7 +5,9 @@ #include "data_tamer_msgs/msg/schemas.hpp" #include "data_tamer_msgs/msg/snapshot.hpp" #include +#include #include +#include "rclcpp_lifecycle/lifecycle_node.hpp" #include #include @@ -15,18 +17,12 @@ namespace DataTamer using PublisherNodeInterfaces = rclcpp::node_interfaces::NodeInterfaces; -// Concept: allow Node, LifecycleNode, or NodeInterface -template -concept NodeInterfaceType = std::same_as || - std::same_as || - std::same_as; - class ROS2PublisherSink : public DataSinkBase { public: - template - ROS2PublisherSink(NodeType interfaces, const std::string& topic_prefix) - : interfaces_(interfaces) + template + ROS2PublisherSink(NodeT&& nodelike, const std::string& topic_prefix) + : node_interface_(normalize_node(nodelike)) { create_publishers(topic_prefix); } @@ -40,9 +36,9 @@ class ROS2PublisherSink : public DataSinkBase const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; schema_publisher_ = rclcpp::create_publisher( - interfaces_, topic_prefix + "/schemas", schemas_qos); + node_interface_, topic_prefix + "/schemas", schemas_qos); data_publisher_ = rclcpp::create_publisher( - interfaces_, topic_prefix + "/data", data_qos); + node_interface_, topic_prefix + "/data", data_qos); } void addChannel(const std::string& name, const Schema& schema) override; @@ -50,6 +46,20 @@ class ROS2PublisherSink : public DataSinkBase bool storeSnapshot(const Snapshot& snapshot) override; private: + template + static PublisherNodeInterfaces normalize_node(NodeT&& nodelike) + { + using D = std::decay_t; + + if constexpr(std::is_same_v) + return nodelike; + else if constexpr(std::is_same_v> || + std::is_same_v>) + return PublisherNodeInterfaces(*nodelike); + else + return PublisherNodeInterfaces(nodelike); + } + std::unordered_map schemas_; Mutex schema_mutex_; @@ -60,7 +70,7 @@ class ROS2PublisherSink : public DataSinkBase data_tamer_msgs::msg::Snapshot data_msg_; // ---- Stored node façade ---- - PublisherNodeInterfaces interfaces_; + PublisherNodeInterfaces node_interface_; }; } // namespace DataTamer From b2a5d8a010ad1f8079e1b9db7c8b6a23ded37299 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Mon, 26 Jan 2026 23:02:30 +0000 Subject: [PATCH 06/14] removing dereference from ros2 pub example, cleaning up ros2 pub header, adding ros2 pub sink tests --- data_tamer_cpp/examples/ros2_publisher.cpp | 2 +- .../data_tamer/sinks/ros2_publisher_sink.hpp | 2 +- data_tamer_cpp/tests/CMakeLists.txt | 2 + data_tamer_cpp/tests/ros2_publisher_tests.cpp | 83 +++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 data_tamer_cpp/tests/ros2_publisher_tests.cpp diff --git a/data_tamer_cpp/examples/ros2_publisher.cpp b/data_tamer_cpp/examples/ros2_publisher.cpp index cba36cb..5e88a59 100644 --- a/data_tamer_cpp/examples/ros2_publisher.cpp +++ b/data_tamer_cpp/examples/ros2_publisher.cpp @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) rclcpp::init(argc, argv); auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(*node, "test"); + auto ros2_sink = std::make_shared(node, "test"); ChannelsRegistry::Global().addDefaultSink(ros2_sink); // Create (or get) a channel using the global registry (singleton) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 8e71503..f6b5536 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -7,7 +7,7 @@ #include #include #include -#include "rclcpp_lifecycle/lifecycle_node.hpp" +#include #include #include diff --git a/data_tamer_cpp/tests/CMakeLists.txt b/data_tamer_cpp/tests/CMakeLists.txt index 6429aa6..80100ea 100644 --- a/data_tamer_cpp/tests/CMakeLists.txt +++ b/data_tamer_cpp/tests/CMakeLists.txt @@ -7,6 +7,7 @@ if(gtest_vendor_FOUND AND ament_cmake_gtest_FOUND) dt_tests.cpp custom_types_tests.cpp parser_tests.cpp + ros2_publisher_tests.cpp trait_tests.cpp) target_include_directories(datatamer_test @@ -28,6 +29,7 @@ else() add_executable(datatamer_test dt_tests.cpp custom_types_tests.cpp + ros2_publisher_tests.cpp parser_tests.cpp) gtest_discover_tests(datatamer_test DISCOVERY_MODE PRE_TEST) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp new file mode 100644 index 0000000..564e54f --- /dev/null +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -0,0 +1,83 @@ +#include "data_tamer/data_tamer.hpp" +#include "data_tamer/sinks/dummy_sink.hpp" +#include "data_tamer/sinks/ros2_publisher_sink.hpp" + +#include + +#include +#include +#include +#include + +using namespace DataTamer; + +TEST(DataTamerROS2Publisher, SharedPointer) +{ + auto node = std::make_shared("test_datatamer"); + auto ros2_sink = std::make_shared(node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) +{ + auto lifecycle_node = std::make_shared("test_" + "datatamer"); + auto ros2_sink = std::make_shared(lifecycle_node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, Dereference) +{ + auto node = std::make_shared("test_datatamer"); + auto ros2_sink = std::make_shared(*node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, DereferenceLifeCycle) +{ + auto lifecycle_node = std::make_shared("test_" + "datatamer"); + auto ros2_sink = std::make_shared(*lifecycle_node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +int main(int argc, char** argv) +{ + rclcpp::init(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + rclcpp::shutdown(); + return ret; +} From 8ae1329cb4837fa2f61837d861b47b092921e282 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Tue, 27 Jan 2026 16:40:30 +0000 Subject: [PATCH 07/14] adding ros2 publisher tests to demonstrate backwards compatibility --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index 564e54f..dd60f3c 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -13,10 +13,10 @@ using namespace DataTamer; TEST(DataTamerROS2Publisher, SharedPointer) { - auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(node, "test"); + auto node = std::make_shared("test_datatamer_shared_pointer"); + auto ros2_sink = std::make_shared(node, "test_shared_pointer"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_shared_pointer"); channel->addDataSink(ros2_sink); @@ -29,10 +29,16 @@ TEST(DataTamerROS2Publisher, SharedPointer) TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) { auto lifecycle_node = std::make_shared("test_" - "datatamer"); - auto ros2_sink = std::make_shared(lifecycle_node, "test"); + "datatamer_" + "shared_" + "pointer_" + "lifecycle"); + auto ros2_sink = std::make_shared(lifecycle_node, "test_shared_" + "pointer_" + "lifecycle"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_shared_pointer_" + "lifecycle"); channel->addDataSink(ros2_sink); @@ -44,10 +50,10 @@ TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) TEST(DataTamerROS2Publisher, Dereference) { - auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(*node, "test"); + auto node = std::make_shared("test_datatamer_dereference"); + auto ros2_sink = std::make_shared(*node, "test_dereference"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_dereference"); channel->addDataSink(ros2_sink); @@ -60,10 +66,14 @@ TEST(DataTamerROS2Publisher, Dereference) TEST(DataTamerROS2Publisher, DereferenceLifeCycle) { auto lifecycle_node = std::make_shared("test_" - "datatamer"); - auto ros2_sink = std::make_shared(*lifecycle_node, "test"); - - auto channel = ChannelsRegistry::Global().getChannel("channel"); + "datatamer_" + "dereference_" + "lifecycle"); + auto ros2_sink = std::make_shared(*lifecycle_node, "test_" + "dereference_" + "lifecycle"); + + auto channel = ChannelsRegistry::Global().getChannel("channel_dereference_lifecycle"); channel->addDataSink(ros2_sink); From 59742475014c1d992ba8d85e74d54db7652917a6 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Fri, 10 Apr 2026 12:42:25 +0000 Subject: [PATCH 08/14] attempting to fix broken Conan build, removing new publisher test executable from Conan block --- data_tamer_cpp/tests/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/data_tamer_cpp/tests/CMakeLists.txt b/data_tamer_cpp/tests/CMakeLists.txt index 80100ea..95cfa31 100644 --- a/data_tamer_cpp/tests/CMakeLists.txt +++ b/data_tamer_cpp/tests/CMakeLists.txt @@ -29,7 +29,6 @@ else() add_executable(datatamer_test dt_tests.cpp custom_types_tests.cpp - ros2_publisher_tests.cpp parser_tests.cpp) gtest_discover_tests(datatamer_test DISCOVERY_MODE PRE_TEST) From 34d9c5d670de3303a023a1d4d502c58c2b59964b Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 18:46:21 +0000 Subject: [PATCH 09/14] remove unused includes --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index dd60f3c..23540bb 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -1,13 +1,9 @@ #include "data_tamer/data_tamer.hpp" -#include "data_tamer/sinks/dummy_sink.hpp" #include "data_tamer/sinks/ros2_publisher_sink.hpp" #include -#include -#include #include -#include using namespace DataTamer; From c7de31d2098399bc098d7808595b00cf76f4d5fa Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 18:47:20 +0000 Subject: [PATCH 10/14] make create_publishers private --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index f6b5536..a834b63 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -27,20 +27,6 @@ class ROS2PublisherSink : public DataSinkBase create_publishers(topic_prefix); } - void create_publishers(const std::string& topic_prefix) - { - rclcpp::QoS schemas_qos{ rclcpp::KeepAll() }; - schemas_qos.reliable(); - schemas_qos.transient_local(); // latch - - const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; - - schema_publisher_ = rclcpp::create_publisher( - node_interface_, topic_prefix + "/schemas", schemas_qos); - data_publisher_ = rclcpp::create_publisher( - node_interface_, topic_prefix + "/data", data_qos); - } - void addChannel(const std::string& name, const Schema& schema) override; bool storeSnapshot(const Snapshot& snapshot) override; @@ -60,6 +46,20 @@ class ROS2PublisherSink : public DataSinkBase return PublisherNodeInterfaces(nodelike); } + void create_publishers(const std::string& topic_prefix) + { + rclcpp::QoS schemas_qos{ rclcpp::KeepAll() }; + schemas_qos.reliable(); + schemas_qos.transient_local(); // latch + + const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; + + schema_publisher_ = rclcpp::create_publisher( + node_interface_, topic_prefix + "/schemas", schemas_qos); + data_publisher_ = rclcpp::create_publisher( + node_interface_, topic_prefix + "/data", data_qos); + } + std::unordered_map schemas_; Mutex schema_mutex_; From e93913ab6e3a76e625dedd1bed708f6247781e10 Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 18:49:59 +0000 Subject: [PATCH 11/14] add test for direct node interfaces construction of ROS 2 publisher --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index 23540bb..66647c7 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -79,6 +79,45 @@ TEST(DataTamerROS2Publisher, DereferenceLifeCycle) EXPECT_TRUE(channel->takeSnapshot()); } +TEST(DataTamerROS2Publisher, NodeInterfacesDirect) +{ + auto node = std::make_shared("test_datatamer_node_interfaces"); + PublisherNodeInterfaces interfaces(*node); + auto ros2_sink = std::make_shared(interfaces, "test_node_" + "interfaces"); + + auto channel = ChannelsRegistry::Global().getChannel("channel_node_interfaces"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, NodeInterfacesDirectLifeCycle) +{ + auto lifecycle_node = std::make_shared("test_" + "datatamer_" + "node_" + "interfaces_" + "lifecycle"); + PublisherNodeInterfaces interfaces(*lifecycle_node); + auto ros2_sink = std::make_shared(interfaces, "test_node_interfaces_" + "lifecycle"); + + auto channel = ChannelsRegistry::Global().getChannel("channel_node_interfaces_" + "lifecycle"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + int main(int argc, char** argv) { rclcpp::init(argc, argv); From fd875b6eef2f851d38a0c03b6c69c9a2924b8186 Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 18:53:49 +0000 Subject: [PATCH 12/14] avoid lifecycle warning on test teardown --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index 66647c7..0184cd8 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -42,6 +42,8 @@ TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) auto id_value = channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); + + lifecycle_node->shutdown(); } TEST(DataTamerROS2Publisher, Dereference) @@ -77,6 +79,8 @@ TEST(DataTamerROS2Publisher, DereferenceLifeCycle) auto id_value = channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); + + lifecycle_node->shutdown(); } TEST(DataTamerROS2Publisher, NodeInterfacesDirect) @@ -116,6 +120,8 @@ TEST(DataTamerROS2Publisher, NodeInterfacesDirectLifeCycle) auto id_value = channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); + + lifecycle_node->shutdown(); } int main(int argc, char** argv) From a0e784942fb70aeed67b5b3f3ed25fde94104361 Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 18:58:01 +0000 Subject: [PATCH 13/14] remove unused variable --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index 0184cd8..00b029e 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -17,7 +17,7 @@ TEST(DataTamerROS2Publisher, SharedPointer) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); } @@ -39,7 +39,7 @@ TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); @@ -56,7 +56,7 @@ TEST(DataTamerROS2Publisher, Dereference) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); } @@ -76,7 +76,7 @@ TEST(DataTamerROS2Publisher, DereferenceLifeCycle) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); @@ -95,7 +95,7 @@ TEST(DataTamerROS2Publisher, NodeInterfacesDirect) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); } @@ -117,7 +117,7 @@ TEST(DataTamerROS2Publisher, NodeInterfacesDirectLifeCycle) channel->addDataSink(ros2_sink); double const value = 1.; - auto id_value = channel->registerValue("value", &value); + channel->registerValue("value", &value); EXPECT_TRUE(channel->takeSnapshot()); From 63862a78b707e1e18a4b4485eef80ba0e863c34d Mon Sep 17 00:00:00 2001 From: Henry Moore Date: Fri, 8 May 2026 19:05:32 +0000 Subject: [PATCH 14/14] add friendly static assert message to normalize_node to help users --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index a834b63..69846df 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -37,13 +37,31 @@ class ROS2PublisherSink : public DataSinkBase { using D = std::decay_t; + // use a friendlier compile error than the one that would otherwise come out + static_assert( + std::is_same_v || + std::is_same_v> || + std::is_same_v> || + std::is_constructible_v, + "ROS2PublisherSink: unsupported node-like type passed to " + "`ROS2PublisherSink(NodeT&& nodelike, const std::string& topic_prefix)`. Pass a " + "rclcpp::Node, " + "rclcpp_lifecycle::LifecycleNode, a shared_ptr to either, or a " + "PublisherNodeInterfaces."); + if constexpr(std::is_same_v) + { return nodelike; + } else if constexpr(std::is_same_v> || std::is_same_v>) + { return PublisherNodeInterfaces(*nodelike); + } else + { return PublisherNodeInterfaces(nodelike); + } } void create_publishers(const std::string& topic_prefix)