Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rclcpp/include/rclcpp/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ class Executor
bool
is_spinning();

RCLCPP_PUBLIC
virtual std::string
get_class_name() const = 0;

protected:
/// Constructor that will not initialize any non-trivial members.
/**
Expand Down
5 changes: 5 additions & 0 deletions rclcpp/include/rclcpp/executors/multi_threaded_executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>

Expand Down Expand Up @@ -73,6 +74,10 @@ class MultiThreadedExecutor : public rclcpp::Executor
size_t
get_number_of_threads();

RCLCPP_PUBLIC
std::string
get_class_name() const override;

Comment on lines +77 to +80
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really do not understand why this needs to be added? even if needed, can we use typeid instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really do not understand why this needs to be added?

Any executor can be used as the underlying executor for ComponentContainer. So even if a user-defined executor is used for ComponentContainer, the value of executor_type should be updated to reflect that.

even if needed, can we use typeid instead?

tbh, I didn't like the get_class_name() approach either. But I was afraid that the value of typeid(exec).name() would be mangled. I wasn't sure what to do :)

protected:
RCLCPP_PUBLIC
void
Expand Down
5 changes: 5 additions & 0 deletions rclcpp/include/rclcpp/executors/single_threaded_executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <cassert>
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>

#include "rclcpp/executor.hpp"
Expand Down Expand Up @@ -65,6 +66,10 @@ class SingleThreadedExecutor : public rclcpp::Executor
void
spin() override;

RCLCPP_PUBLIC
std::string
get_class_name() const override;

private:
RCLCPP_DISABLE_COPY(SingleThreadedExecutor)
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <atomic>
#include <chrono>
#include <memory>
#include <string>
#include <vector>

#include "rclcpp/executor.hpp"
Expand Down Expand Up @@ -126,6 +127,10 @@ class EventsExecutor : public rclcpp::Executor
void
spin_all(std::chrono::nanoseconds max_duration) override;

RCLCPP_PUBLIC
std::string
get_class_name() const override;

protected:
/// Internal implementation of spin_once
RCLCPP_PUBLIC
Expand Down
6 changes: 6 additions & 0 deletions rclcpp/src/rclcpp/executors/multi_threaded_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ MultiThreadedExecutor::get_number_of_threads()
return number_of_threads_;
}

std::string
MultiThreadedExecutor::get_class_name() const
{
return "MultiThreadedExecutor";
}

void
MultiThreadedExecutor::run([[maybe_unused]] size_t this_thread_number)
{
Expand Down
6 changes: 6 additions & 0 deletions rclcpp/src/rclcpp/executors/single_threaded_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ SingleThreadedExecutor::spin()
}
}
}

std::string
SingleThreadedExecutor::get_class_name() const
{
return "SingleThreadedExecutor";
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ EventsExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
}
}

std::string
EventsExecutor::get_class_name() const
{
return "EventsExecutor";
}

void
EventsExecutor::execute_event(const ExecutorEvent & event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class CustomExecutor : public rclcpp::Executor

void spin() override {}

std::string get_class_name() const override
{
return "CustomExecutor";
}

void collect()
{
this->collect_entities();
Expand Down
5 changes: 5 additions & 0 deletions rclcpp/test/rclcpp/test_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class DummyExecutor : public rclcpp::Executor
{
}

std::string get_class_name() const override
{
return "DummyExecutor";
}

void spin_nanoseconds(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
{
spin_node_once_nanoseconds(node, std::chrono::milliseconds(100));
Expand Down
21 changes: 16 additions & 5 deletions rclcpp_components/include/rclcpp_components/component_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ class ComponentManager : public rclcpp::Node
using ComponentResource = std::pair<std::string, std::string>;

/// Default constructor
/**
* Initializes the component manager. It creates the services: load node, unload node
* and list nodes. Initialize executor after construction using set_executor.
*
* \param executor the executor which will spin the node.
* \param node_name the name of the node that the data originates from.
* \param node_options additional options to control creation of the node.
*/
RCLCPP_COMPONENTS_PUBLIC
ComponentManager(
std::string node_name = "ComponentManager",
const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions());
Comment on lines +93 to +104
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you tell me why this new ctor is required to add?


/// Constructor with executor
/**
* Initializes the component manager. It creates the services: load node, unload node
* and list nodes.
Expand All @@ -100,12 +114,9 @@ class ComponentManager : public rclcpp::Node
*/
RCLCPP_COMPONENTS_PUBLIC
ComponentManager(
std::weak_ptr<rclcpp::Executor> executor =
std::weak_ptr<rclcpp::executors::MultiThreadedExecutor>(),
std::weak_ptr<rclcpp::Executor> executor,
std::string node_name = "ComponentManager",
const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions()
.start_parameter_services(false)
.start_parameter_event_publisher(false));
const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions());
Comment on lines -103 to +119
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this breaks the API/ABI, we should deprecate the ctor 1st before breaking changes. see https://docs.ros.org/en/rolling/The-ROS2-Project/Contributing/Developer-Guide.html#deprecation-strategy

Copy link
Author

@armaho armaho Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh, I did not understand the default value for the executor parameter. Isn't this default value destructed right after the constructor is done? and there's no way for user to actually get this executor to call spin on it. so the user either have to use:

exec = some type of executor
ComponentManager(exec)

which is covered with this constructor, or do something like:

cm = ComponentManager()
cm.set_executor(exec)

which is covered with the other constructor I added. Am I right?


RCLCPP_COMPONENTS_PUBLIC
virtual ~ComponentManager();
Expand Down
28 changes: 26 additions & 2 deletions rclcpp_components/src/component_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,32 @@ int main(int argc, char * argv[])
{
/// Component container with a single-threaded executor.
rclcpp::init(argc, argv);
auto exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);

std::shared_ptr<rclcpp::Executor> exec;
auto node = std::make_shared<rclcpp_components::ComponentManager>();

const auto exec_type = node->get_parameter("executor_type").as_string();
if (exec_type == "SingleThreadedExecutor" || exec_type == "No Executor") {
// Default executor for ComponentContainer is SingleThreadedExecutor, because of
// backward compatibility.
exec = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
} else if (exec_type == "MultiThreadedExecutor") {
if (node->has_parameter("thread_num")) {
const auto thread_num = node->get_parameter("thread_num").as_int();
exec = std::make_shared<rclcpp::executors::MultiThreadedExecutor>(
rclcpp::ExecutorOptions{}, thread_num);
} else {
exec = std::make_shared<rclcpp::executors::MultiThreadedExecutor>();
}
} else if (exec_type == "EventsExecutor") {
exec = std::make_shared<rclcpp::experimental::executors::EventsExecutor>();
} else {
RCLCPP_ERROR(node->get_logger(), "Unknown executor type %s", exec_type.c_str());
return 1;
}

node->set_executor(exec);

exec->add_node(node);
exec->spin();

Expand Down
10 changes: 10 additions & 0 deletions rclcpp_components/src/component_container_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@

#include "rclcpp_components/component_manager.hpp"

[[deprecated("component_container_event is deprecated and will be removed "
"in the next version. Use 'ros2 run rclcpp_components "
"component_container --ros-args -p executor_type:=EventsExecutor' "
"instead.")]]
int main(int argc, char * argv[])
{
/// Component container with an events executor.
rclcpp::init(argc, argv);
auto exec = std::make_shared<rclcpp::experimental::executors::EventsExecutor>();
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);

RCLCPP_WARN(node->get_logger(),
"component_container_event is depracated and will be removed "
"in the next version. use 'ros2 run rclcpp_components "
"component_container --ros-args -p executor_type:=EventsExecutor'.");

Comment on lines +32 to +37
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we instead use [[deprecated("message")]] attribute?

Copy link
Author

@armaho armaho Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried that, it showed nothing when I used ros2 run to run the component container. I will add the deprecated attribute but I think it's good to also keep the warning.

exec->add_node(node);
exec->spin();

Expand Down
12 changes: 12 additions & 0 deletions rclcpp_components/src/component_container_mt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

#include "rclcpp_components/component_manager.hpp"

[[deprecated("component_container_event is depracated and will be removed "
"in the next version. use 'ros2 run rclcpp_components "
"component_container --ros-args -p executor_type:="
"MultiThreadedExecutor'."
)]]
int main(int argc, char * argv[])
{
/// Component container with a multi-threaded executor.
Expand All @@ -34,6 +39,13 @@ int main(int argc, char * argv[])
} else {
node->set_executor(exec);
}

RCLCPP_WARN(node->get_logger(),
"component_container_event is depracated and will be removed "
"in the next version. use 'ros2 run rclcpp_components "
"component_container --ros-args -p executor_type:="
"MultiThreadedExecutor'.");

exec->add_node(node);
exec->spin();

Expand Down
24 changes: 21 additions & 3 deletions rclcpp_components/src/component_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ namespace rclcpp_components
{

ComponentManager::ComponentManager(
std::weak_ptr<rclcpp::Executor> executor,
std::string node_name,
const rclcpp::NodeOptions & node_options)
: Node(std::move(node_name), node_options),
executor_(executor)
: Node(std::move(node_name), node_options)
{
loadNode_srv_ = create_service<LoadNode>(
"~/_container/load_node",
Expand All @@ -75,6 +73,22 @@ ComponentManager::ComponentManager(
this->declare_parameter(
"thread_num", static_cast<int64_t>(std::thread::hardware_concurrency()), desc);
}

rcl_interfaces::msg::ParameterDescriptor exec_type_desc{};
exec_type_desc.description = "Type of executor used for ComponentManager";
exec_type_desc.type = rclcpp::PARAMETER_STRING;
// The value is changed only within set_executor. It cannot be changed externally.
exec_type_desc.read_only = true;
this->declare_parameter("executor_type", "No Executor", exec_type_desc);
Comment on lines +77 to +82
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need to do this including set_executor change? once we create the node (ComponentManager), we are able to get the parameters via command line interfaces? that was the original plan? and then, create the corresponding executor and bind it to the node (ComponentManager)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry I don't quite understand this. Are you asking about the comment I added here, or the changes I made to set_executor?

}

ComponentManager::ComponentManager(
std::weak_ptr<rclcpp::Executor> executor,
std::string node_name,
const rclcpp::NodeOptions & node_options)
: ComponentManager::ComponentManager(node_name, node_options)
{
this->set_executor(executor);
}

ComponentManager::~ComponentManager()
Expand Down Expand Up @@ -243,6 +257,10 @@ void
ComponentManager::set_executor(const std::weak_ptr<rclcpp::Executor> executor)
{
executor_ = executor;
if (auto exec = executor_.lock()) {
rclcpp::Parameter exec_type("executor_type", exec->get_class_name());
auto result = this->set_parameter(exec_type);
}
}

void
Expand Down