Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

template node type for rclcpp action server and clients #892

Merged
merged 3 commits into from
Oct 17, 2019
Merged

template node type for rclcpp action server and clients #892

merged 3 commits into from
Oct 17, 2019

Conversation

SteveMacenski
Copy link
Collaborator

@SteveMacenski SteveMacenski commented Oct 14, 2019

Addresses #846 and has the rclcpp::Node as the default template argument so it shouldn't break users of it.

I'd add a second test to work with lifecycle nodes, but I didn't want to add lifecycle as a dependency to actions.

Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
@Karsten1987
Copy link
Contributor

Karsten1987 commented Oct 14, 2019

Why not templating it to any pointer type? There is a type trait within rcpputils which could be used for a static assert.
But I believe that forcing a shared pointer would still not work to address the shared_from_this() problem within the constructor when used within a class inheriting from rclcpp::Node. Whereas a raw pointer to this would.
I might be wrong though.

EDIT: Also I feel the tests should be enhanced to actually create a actionclient/server from a node constructor.

@SteveMacenski
Copy link
Collaborator Author

@Karsten1987 looking across the repo, all instances I can find of node and message type templating are all base types that are later called with T::SharedPtr. I don't see why actions should differ. If this one was templated as a pointer, we'd have to template them all that way for consistency.

A few examples:
https://github.com/ros2/rclcpp/blob/master/rclcpp/include/rclcpp/create_service.hpp#L33

auto node = std::make_shared<NodeT>(options);

template<typename NodeT = rclcpp::Node, typename RepT = int64_t, typename T = std::milli>

Similarly, I'm not seeing anywhere in here doing a static_assert checking if ptr for creating comms, create_*, it seems inconsistent to do that here and no where else. If you try to give it a non-shared ptr it'll fail to compile so I'm not really afraid of asserting here.

Similar statements for the shared_from_this() - that seems out of scope and the design of a node can be simply done to avoid creating in the constructor, the same way you'd have to for any other overload using the shared ptr.

I'm trying to resolve the missing feature of rclcpp_action not being able to take in various node types to make navigation2 code cleaner. Looking back on the ticket, it looks like they wanted something else - removing the addressing in the PR description

@jacobperron
Copy link
Member

jacobperron commented Oct 14, 2019

+1 for a more generic template type. E.g. similar to rclcpp publishers and subscriptions:

template<
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>,
typename NodeT>
std::shared_ptr<PublisherT>
create_publisher(
NodeT & node,
const std::string & topic_name,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
)
)

Which passes the node to get_node_topics_interfaces() (which has templates for pointer and reference types):

/// Get the NodeTopicsInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>

template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>

Assuming the proper template functions already exist for the various interfaces, we should be able to make the change:

-  rclcpp::Node::SharedPtr node,
+  NodeT node

And you can default NodeT to rclcpp::Node::SharedPtr.

I haven't tried it, but maybe we have to use free functions for getting the interfaces instead of the node methods (edit: I think we certainly will if we also want to support passing nodes by reference), e.g.

get_node_base_interface(node);

instead of

node->get_node_base_interface();

@SteveMacenski
Copy link
Collaborator Author

SteveMacenski commented Oct 15, 2019

@jacobperron I've read that a few times and I'm not entirely certain if you're suggesting a change or expressing a line of thought. Is there something specific you'd like me to do? I'm hearing maybe change from node->get_xyz() to get_xyz(node) and templating in the ptr type.

I intentionally didn't template the SharedPtr because it seemed more readable from user code to do rclcpp_action::create_client<rclcpp_lifecycle::LifecycleNode> than rclcpp_action::create_client<rclcpp_lifecycle::LifecycleNode::SharedPtr> but I suppose I don't have a strong opinion beyond just convention I generally follow.

@jacobperron
Copy link
Member

I'm not entirely certain if you're suggesting a change or expressing a line of thought. Is there something specific you'd like me to do?

Sorry, I guess it was a bit of both. I'm no wizard with templates, but I would expect the following definition to work not only for shared pointers, but a variety of pointer and reference types for node-like objects (ie. objects that implement the required node interfaces):

template<typename ActionT, typename NodeT>
typename Client<ActionT>::SharedPtr
create_client(
  NodeT node,
  const std::string & name,
  rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
  return create_client<ActionT>(
    rclcpp::node_interfaces::get_node_base_interface(node),
    rclcpp::node_interfaces::get_node_graph_interface(node),
    rclcpp::node_interfaces::get_node_logging_interface(node),
    rclcpp::node_interfaces::get_node_waitables_interface(node),
    name,
    group);
}

Then, I think template argument deduction will make it so we don't need to worry about specifying the template type. E.g.

// 'node' is a node-like thing that could be a pointer or passed in by reference
auto client = rclcpp_action::create_client<MyAction>(node, "action_name");

I'd like to see the create_client and create_server functions updated to look like what I've got above. I haven't tested it, so I'm not sure if it works out-of-the-box with lifecycle nodes.

@jacobperron
Copy link
Member

I just realized that to do what I proposed requires the existence of the following functions:

  • get_node_graph_interface
  • get_node_logging_interface
  • get_node_waitables_interface

I guess we could add those missing free functions.

@SteveMacenski
Copy link
Collaborator Author

I guess we could add those missing free functions.

I leave it up to you, if you feel that's necessary to merge this I can take a crack at it. I wanted to get this in before the Friday freeze so I scoped it pretty low. I can file a ticket to implement the other free methods and revisit this topic as well at a later time. I am also not a templating wizard, but I'm willing to try to become one ;-)

@SteveMacenski
Copy link
Collaborator Author

Alright cool sounds like I have the concrete action items 👍

Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
@SteveMacenski
Copy link
Collaborator Author

SteveMacenski commented Oct 16, 2019

@jacobperron changes made. Everything seems to be fine. CI failures unreleased to this

Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

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

LGTM, just one last nitpick.

rclcpp_action/test/test_client.cpp Outdated Show resolved Hide resolved
Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
@SteveMacenski
Copy link
Collaborator Author

SteveMacenski commented Oct 16, 2019

@jacobperron 👍

Real stickler. I like it. This should be squashed now with these many small commits

Copy link
Member

@jacobperron jacobperron left a comment

Choose a reason for hiding this comment

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

@SteveMacenski Thanks for iterating!

I think the PR CI jobs will continue to fail until the latest changes in rclcpp are released into Eloquent.
I'll trigger CI manually:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

Note, I expect some pub/sub communication tests to fail due to a known issue.

@jacobperron
Copy link
Member

This should be squashed now with these many small commits

We typically do a squash merge 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants