ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
23

Why is everything a shared_ptr in ROS2?

asked 2020-10-09 07:48:53 -0600

nnmm gravatar image

updated 2020-10-09 08:05:12 -0600

Many functions in rclcpp return a std::shared_ptr, e.g. create_subscription(), or take a std::shared_ptr argument, e.g. subscriber callbacks. This surprises me because it's my impression that it's good practice to use shared pointers only when needed, and prefer a unique_ptr or even plain objects.

For instance, the std::shared_ptr returned by create_subscription() appears to be the sole owner of the subscriber, so it could be a std::unique_ptr instead. Besides being a bit more performant and flexible, this would also serve as documentation: I had to write a test case to find out that there is only one owner and that you can therefore shut down a subscription by resetting it to nullptr, but if it had been a std::unique_ptr, that would have been obvious. Similarly, everywhere a pointer is used, it theoretically allows for a nullptr, but if a function returned a plain object or accepted arguments by reference instead, users of the API could rely on the object never being null.

I'm not sure if plain objects would be possible – maybe rclcpp uses polymorphism and SubscriptionT is only a base class, while the true type is hard/impossible to name?

So why is everything a shared pointer in ROS2? I hope a broad question like makes sense, but if not, my main questions would be:

  • Why are there no versions of create_subscription() and create_publisher() that return unique pointers or plain objects?
  • Why do the subscription callbacks not receive messages by reference?
edit retag flag offensive close merge delete

Comments

I'd also be interested in some discussion surrounding this. It's also easy to convert a unique_ptr to a shared_ptr if shared ownership is needed during runtime. My guess is that it's done this way to simplify the overall interface during the initial development of ROS2. This sets a bad precedent for clear ownership however, as you have pointed out.

oysstu gravatar image oysstu  ( 2020-11-06 04:48:27 -0600 )edit

Any updates on this? My team is trying to make the switch to ROS2 but have the exact same questions ...

jn42 gravatar image jn42  ( 2021-09-22 14:13:19 -0600 )edit

For an authoritative answer, we'll need input from someone who was present when this design choice was made.

Perhaps @William, or one of the other "core devs" of ROS 2.

gvdhoorn gravatar image gvdhoorn  ( 2021-09-22 14:22:31 -0600 )edit

Though not a direct and complete answer to your question, when I was searching for the same question I found the ROS 2 design article Intra-process Communications in ROS 2 that provides interesting information on using different type of smart pointers for publishers and subscribers

mateussmenezes gravatar image mateussmenezes  ( 2022-11-16 17:16:12 -0600 )edit

2 Answers

Sort by » oldest newest most voted
0

answered 2020-11-17 04:35:13 -0600

fredBeauj gravatar image

I wasn't involved in the design decision but asked myself the same question as @nnmm . One thing I noticed is that shared pointers are pervasive in that if you use shared_from_thisinside the class definition (and that happens quite a bit) so the node can pass itself to members, then it would be undefined behavior until C++17 if the node wasn't created as a shared pointer; cf https://en.cppreference.com/w/cpp/mem...

Certainly not great to require knowing how your instance is created/stored but that's how it is.

edit flag offensive delete link more
0

answered 2023-02-26 12:38:44 -0600

hussein_mo gravatar image

In ROS2, shared pointers are used mainly for memory management and thread safety. By using shared pointers, the memory associated with the object is automatically freed when the last reference to it is destroyed, avoiding memory leaks. Shared pointers also provide thread-safe access to the objects they manage, which is important for a distributed system like ROS2.

The use of shared pointers for create_subscription() and create_publisher() is necessary to ensure that the objects they create can be accessed and used safely by multiple threads. As for the subscription callbacks, messages are passed by reference so that the user can modify the message if necessary. This is important for applications such as message filters, where the user may need to modify the message before passing it on.

Overall, while there are some performance and readability advantages to using unique pointers or plain objects, shared pointers are the safest option for ROS2 given the distributed nature of the system.

edit flag offensive delete link more

Comments

Shared pointers provide thread safe increment/decrement of reference counters, as well as ensuring that only one thread calls the destructor. It does not provide thread safe access to the objects they manage.

oysstu gravatar image oysstu  ( 2023-02-27 04:29:59 -0600 )edit

sorry but this is not correct. As @oysstu mentions, shared_ptr's control block is thread safe, but simply accessing an object through a shared_ptr does not make that access thread safe. (I believe there's an upcoming c++ proposal to add a pointer that does this, but am not sure what the status of that is)

I agree that overall the design of rclcpp is a _massive_ abuse of shared_ptrs and should in general have not used pointers at all. I assume this was probably to maintain design patterns from ros1 which is unfortunate.

strike_eagle_iii gravatar image strike_eagle_iii  ( 2023-05-02 08:39:24 -0600 )edit

Question Tools

14 followers

Stats

Asked: 2020-10-09 07:48:53 -0600

Seen: 5,504 times

Last updated: Nov 17 '20