Skip to content

Conversation

@oysstu
Copy link
Contributor

@oysstu oysstu commented Jan 5, 2026

Description

This PR adds a constexpr-capable MemberTraits structure such that the number of members and member names can be used in a compile-time context. The PR also adds a function that forwards the members of the struct as a tuple of references. This makes it very simple to treat the members generically, using for example std::apply.

Implements #923

Is this user-facing behavior change?

It should not, as the new struct / function is contained within the appropriate namespaces.

Did you use Generative AI?

No

Copy link
Contributor

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

i think this is useful and non-breaking change, lgtm with green CI.

i also think that alternative approach would be traits class (like rosidl_generator_traits) rather than adding members to the message itself. this keeps the message "cleaner" but requires more boilerplate for users.

a couple of comments,

(not blocking this PR) Does this also apply to service request/response structs and action types? The issue only mentions messages.
Users won't know this feature exists without documentation. Should be mentioned in the rosidl_generator_cpp README or official documentation?

@oysstu
Copy link
Contributor Author

oysstu commented Jan 6, 2026

i also think that alternative approach would be traits class (like rosidl_generator_traits) rather than adding members to the message itself. this keeps the message "cleaner" but requires more boilerplate for users.

Yes, good point. What about something like the following in msg__traits.hpp.em?

namespace rosidl_generator_traits
{

template<>
struct MessageTraits<rosidl_generator_tests::msg::Defaults> {
  static constexpr std::size_t member_count = 13;
  static constexpr std::array<std::string_view, member_count> member_names = {"bool_value", ...};
}

}

I also propose the following addition, as this function greatly reduces the amount of boilerplate needed to do generic programming over the structs. Without it, one must implement a structured binding to tuple function for each possible number of members. It works well, but a maximum member count has to be decided on, which is avoided by defining the function below.

namespace rosidl_generator_traits
{

template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, rosidl_generator_tests::msg::Defaults>, int> = 0>
constexpr auto as_tuple_ref(T&& msg) {
    return std::forward_as_tuple(std::forward<T>(msg).bool_value, ..., std::forward<T>(msg).uint64_value);
}

}

Does this also apply to service request/response structs and action types? The issue only mentions messages.

When defined in the msg__struct.hpp.em or msg__traits.hpp.em templates, yes, because that is used to generate the definitions for the underlying Response/Request messages. One would use the is_service/is_action traits and then access the underlying message structs.

Users won't know this feature exists without documentation. Should be mentioned in the rosidl_generator_cpp README or official documentation?

Yes, should probably add some documentation once we agree on the design.

@kscottz
Copy link

kscottz commented Jan 22, 2026

As per Waffle meeting this needs to go to the PMC.

@oysstu
Copy link
Contributor Author

oysstu commented Jan 25, 2026

Great, I've updated the example above to use forward_as_tuple, in order to be more flexible. I will convert this to a draft, and revise the implementation after the PMC has looked at it.

@oysstu oysstu marked this pull request as draft January 25, 2026 14:40
@asymingt asymingt self-requested a review January 26, 2026 21:23
@asymingt
Copy link
Member

asymingt commented Jan 26, 2026

When this was raised in last week's PMC the consensus seemed to be that the change looks really useful. Thanks for putting up this PR. Looks like all checks pass, and @fujitatomoya has already given feedback. So, I'm happy to mark it LGTM. Since rosidl_generator_cpp doesn't have any documentation, can we put something on this page making reference to the traits? ie. update the page here with an accompanying PR.

@oysstu
Copy link
Contributor Author

oysstu commented Jan 27, 2026

@asymingt I marked it as draft, because this PR does not yet contain the changes proposed by @fujitatomoya. I will update the PR and request another review.

@oysstu oysstu force-pushed the oysstu/cpp-struct-constexpr-metadata branch from 8d74060 to a60cf47 Compare January 27, 2026 17:34
@oysstu oysstu marked this pull request as ready for review January 27, 2026 17:35
@oysstu oysstu requested a review from fujitatomoya January 27, 2026 17:35
@oysstu oysstu changed the title rosidl_generator_cpp: static constexpr arity and names in generated structs rosidl_generator_cpp: constexpr message traits and to_tuple_ref in generated structs Jan 27, 2026
@oysstu
Copy link
Contributor Author

oysstu commented Jan 27, 2026

Ok, I've updated the PR with the proposed changes, please take a look.

I was hoping to make a PR to follow up this one to change the message struct constructors to be constexpr, but that is not possible until C++20 due to the if/else.

@oysstu oysstu changed the title rosidl_generator_cpp: constexpr message traits and to_tuple_ref in generated structs rosidl_generator_cpp: constexpr message traits and to_tuple_ref for generated structs Jan 27, 2026
@ros-discourse
Copy link

This pull request has been mentioned on Open Robotics Discourse. There might be relevant details there:

https://discourse.openrobotics.org/t/ros-pmc-minutes-for-january-27-2026/52151/1

@asymingt
Copy link
Member

asymingt commented Jan 28, 2026

i think this is useful and non-breaking change, lgtm with green CI.

i also think that alternative approach would be traits class (like rosidl_generator_traits) rather than adding members to the message itself. this keeps the message "cleaner" but requires more boilerplate for users.

a couple of comments,

(not blocking this PR) Does this also apply to service request/response structs and action types? The issue only mentions messages. Users won't know this feature exists without documentation. Should be mentioned in the rosidl_generator_cpp README or official documentation?

I believe srv_traits internally calls the msg_traits, and so we receive the benefit on the Request and Response sub-messages. A similar thing happens for action Goals, Feedback and Result here.

Copy link
Member

@asymingt asymingt left a comment

Choose a reason for hiding this comment

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

LGTM s.t a green build. Let's please update the documentation (PR against a separate repo) per my earlier comment.

@asymingt
Copy link
Member

asymingt commented Jan 28, 2026

Had some CI issues in the last 48 hours. Here's the re-trigger!

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@asymingt
Copy link
Member

@fujitatomoya, I'm going to hold of merging until you approve the changes you requested. Feel free to merge, if the CI above all goes 🟢 🟢 🟢 🟢

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.

5 participants