-
Notifications
You must be signed in to change notification settings - Fork 124
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
Add a utility for rigorously initializing a message instance #448
Add a utility for rigorously initializing a message instance #448
Conversation
Signed-off-by: Michael X. Grey <grey@openrobotics.org>
Signed-off-by: Michael X. Grey <grey@openrobotics.org>
Tests are added, so I'd say this is ready for review. My one remaining concern is how many lines of code the initialization structs take up in the class definition. That could be flattened out significantly if we want to define a macro in a commonly shared header. Otherwise if we're not concerned about how many lines of code are being consumed, then this PR is good to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
If only we had meta-classes... https://www.fluentcpp.com/2017/08/04/metaclasses-cpp-summary/
Before merging this I would be curious if we can move this into a separate header - simply to keep the |
I agree with wanting to make the header more readable. I can see two directions we can take:
And there'd be a fourth macro called Both options would occupy the same number of lines of code inside the class definition. Option (1) would add significant complexity to the file generation but it would scream at you less inside the class definition. Option (2) screams at you a bit in the class definition, but it would require absolutely no changes to the file generation scheme. I'm more inclined towards option (2). It'll be very easy to draft, so I'll make those changes really quick, and if anyone objects to it we can revert afterwards. |
Signed-off-by: Michael X. Grey <grey@openrobotics.org>
The generated message headers should be much smaller now. Here's the new snippet from
It's still a bit noisy, but I think it's roughly the same noise level as the rest of the generated header. |
Signed-off-by: Michael X. Grey <grey@openrobotics.org>
Is there anything left to do for this PR? Does anyone have an opinion about the new macro approach? |
I still want to explore an alternative approach which is implemented in a separate header without having to inject all the macros into the struct header. |
I think that would be undesirable because we'd be polluting the message namespace with lots of class names, since every field of every message requires its own class. Users of auto-complete (like myself) would see their auto-complete hammered with something like
We could hide that by putting those all into some kind of Maybe there's a creative solution that I'm not thinking of, but I'm very skeptical that we'll get something cleaner than this macro approach. |
I also think it's worth pointing out that these macros take up fewer lines of code in the header than even the field type aliases do. |
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
See 656165b which moves the builder functionality into a completely separate header. At the moment you have to The test is using the function |
I can understand wanting this separation, but I really think the previous API had better ergonomics:
I guess these details are ultimately about personal preference and aesthetics, which are notoriously difficult things to find a consensus on. So if others think this separation is aesthetically/ergonomically preferable, then I suppose it's fine with me. But I would definitely advise against making a |
As mentioned above: "At the moment you have to
There is no "completely separate class". I propose either a global function named after the data class or a templated function. The same approach is already being applied for e.g. the function. They are being defined in a separate header to keep each file focused on one thing. It also allows to use the struct without any of the additional parts (builder, functions, whatever else) to keep it as light weight as possible. So if someone doesn't want to use the builder there is no need to test / certify that code.
The fact that a file is already long sounds like a bad rational for justifying even more to it. |
+1 for including
I don't care if it's a global function or a method on the class, but I do think the shed would look nicer having the message namespace leading up to the builder, e.g. geometry_msgs::msg::build_Point()
// or
geometry_msgs::msg::build::Point()
// or
geometry_msgs::msg::Point::build() instead of: rosidl_generator_cpp::build<geometry_msgs::Point>() |
|
@dirk-thomas I've edited my comment, I didn't meant to have the extra struct name in the namespace. |
That is the proposed API in the latest commit from me.
This would be possible. The Naming of
Afaik this is only possible when being defined in the struct (which as mentioned above for separation of concern / avoiding optional API to become mandatory I would rather like to not do).
I am happy to not follow up on this and try to make it work if it is undesired anyway. |
I think if I were to choose between |
To be super pedantic, it only requires that the function is declared in the struct (and that the initial return type is forward declared somewhere). It could be declared inside the struct and then defined in a separate header. But then that leads to confusion if people try to use it without having all the necessary headers included, so I'd say that's probably the worst combination of design choices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to geometry_msgs::msg::build::Point
if index < len(message.structure.members) - 1: | ||
next_field_name = message.structure.members[index + 1].name | ||
}@ | ||
class Init_@(message.structure.namespaced_type.name)_@(field_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd think that builders should be templated by allocator type just like interface structs are (see here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the risk of beating a dead horse, we would get this trivially if we used the earlier static member function approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since none of the ROS 2 API works at the moment with custom allocators I would rather not make this more complicated and just leave custom allocators out of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I do see rclcpp::Publisher
propagating the given AllocatorT
accordingly. I don't see any MessageT
allocator being used though. So I'm fine with not doing anything else on this PR, but a follow-up issue to track the missing (or half-implemented or to be deprecated) feature would be nice.
That is not possible since the return type is not a pointer / reference but a value so it can't be forward declared. |
…ttern Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
We discussed the possible option in the ROS meeting:
|
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides @hidmic's comment, LGTM
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM but for a few comments and pending green CI
Above CI is green (considering the yellow builds with unrelated test failures). |
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
I added special handling for |
This implements the feature request made here.
I still have to write tests, which I assume should go in here. I wanted to open this PR right away in case anyone has an objection to the implementation.
One thing I'd appreciate feedback on is: Should the implementations of the field initializer structs go into a separate implementation header to avoid bloating the
__struct.hpp
header? We could declare the field initializer structs as nested classes while putting their definitions in a separate header that automatically gets included. I think it's mostly a question of aesthetics, although splitting out the header will add some non-trivial complexity to the header generation.