-
Notifications
You must be signed in to change notification settings - Fork 406
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
Can't set initial parameters when allowing undeclared #730
Comments
What is |
Added to the description |
I haven't been able to reproduce the bug you're reporting, using master. Initial parameters aren't supposed to be automatically declared, except you use rclcpp/rclcpp/include/rclcpp/node_options.hpp Lines 260 to 272 in 30df5cd
If you add a std::vector<rclcpp::Parameter> initial_parameters =
{rclcpp::Parameter("my_string", std::string("str"))};
rclcpp::NodeOptions node_options;
node_options.allow_undeclared_parameters(true);
node_options.initial_parameters(initial_parameters);
auto node = std::make_shared<Node>("name", "ns", node_options);
printf("has_parameter: %d\n", node->has_parameter("my_string")); // prints 0 If you add the following line before creating the node: node_options.automatically_declare_initial_parameters(true); You will see that And actually, rclcpp/rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp Lines 554 to 585 in 30df5cd
The exception you are seeing came from a call you are doing later to auto param = node->get_parameter("my_string");
param.get_value<std::string>(); // throws if you haven't set automatically_declare_initial_parameters If this not answer your question, please share the complete example. Thanks! |
But isn't this weird that even though we've set the initial parameters, the call to |
Maybe, @wjwwood any opinions? Edit: |
I can work on a better example soon.
+1 to this The documentation says
So I would expect that to also happen to undeclared parameters set through the constructor without needing to also set |
I cannot reproduce the issue in the original post, Either way, I'd recommend these as the "golden rules" for parameters in Dashing:
I think the fundamental misunderstanding is that The
That means when you, the user, set a value for a parameter (using
I think you're misunderstanding It does not allow for parameters to be declared automatically just because they're passed as initial parameters or in the YAML file.
This came up in the original review and I don't think anything has changed since then. There are cases where both are enabled, one or the other is enabled, and when both are disabled that all make sense. I don't think we should couple their behavior. If you want both behaviors, then enable them both. I believe I've covered this topic quite thoroughly in the migration docs: https://index.ros.org/doc/ros2/Releases/Release-Dashing-Diademata/#declaring-parameters If you guys are still confused by it then we definitely need to update it, but I honestly don't know how I could have made it clearer. My example (it compiles and runs without error on master): #include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.allow_undeclared_parameters(true);
options.initial_parameters({{"bar", "baz"}});
auto node = std::make_shared<rclcpp::Node>("test_node", "", options);
node->get_parameter("foo");
return 0;
}
|
Furthermore, as @ivanpauno pointed out, the exception you're getting is not due to it not being declared first, it is due to you trying to coerce it into a string when it has no value. |
Failing test: diff --git a/rclcpp/test/test_node.cpp b/rclcpp/test/test_node.cpp
index bb3ac35..95cddfe 100644
--- a/rclcpp/test/test_node.cpp
+++ b/rclcpp/test/test_node.cpp
@@ -722,6 +722,24 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_allowed) {
}
}
+TEST_F(TestNode, initial_parameters_undeclared_parameters_allowed) {
+ rclcpp::NodeOptions node_options;
+ node_options.allow_undeclared_parameters(true);
+ node_options.initial_parameters({rclcpp::Parameter("my_string", std::string("str"))});
+ auto node = std::make_shared<rclcpp::Node>("test_set_parameter_node"_unq, node_options);
+ auto value = node->get_parameter("my_string").get_value<std::string>();
+ EXPECT_EQ("str", value);
+}
+
+TEST_F(TestNode, set_parameters_undeclared_parameters_allowed_initial) {
+ rclcpp::NodeOptions node_options;
+ node_options.allow_undeclared_parameters(true);
+ auto node = std::make_shared<rclcpp::Node>("test_set_parameter_node"_unq, node_options);
+ node->set_parameters({rclcpp::Parameter("my_string", std::string("str"))});
+ auto value = node->get_parameter("my_string").get_value<std::string>();
+ EXPECT_EQ("str", value);
+}
+
TEST_F(TestNode, set_parameters_undeclared_parameters_not_allowed) {
auto node = std::make_shared<rclcpp::Node>(
"test_set_parameters_node"_unq,
The reason why one test fails but the other passes is esoteric at best 🔮 The use case I have on
Since this sounds like a strong recommendation and the first-class supported behaviour, I'll go for option 2.
But if the user is choosing to work with undeclared parameters, then the user shouldn't need to declare them 🤷♀️
That's exactly where I learned about that option. And it says "you can get the old behavior by using the allow_undeclared_parameters option when creating your node. You might want to do this in order to avoid code changes for now". Unfortunately it didn't go quite how I expected 😬
I think the whole confusion is coming from the fact that With the info on this issue, now I understand when the current documentation says that "these initial parameter values are used to change the initial value of declared parameters within the node, overriding hard coded default values if necessary." I think that someone looking at this for the first time may not realize the importance of the word declared in that sentence. It may be worth elaborating a bit more. I'm also strongly on the side of automatically declaring all parameters when As a side note, I imagine this has been discussed at length, but throwing an exception to save users from a typo feels a bit too strict to me. My 2 cents 🙂 |
The first test you added exposes a definite bug, imo. It wasn't clear to me because the example from the original issue description didn't declare the type of
@ivanpauno and I suggested as much, but I didn't understand until now that that was actually incorrect. Your first new test should pass and doesn't. I'll work on a patch for that now. My updated example does fail: #include <string>
#include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.allow_undeclared_parameters(true);
options.initial_parameters({{"bar", "baz"}});
auto node = std::make_shared<rclcpp::Node>("test_node", "", options);
std::string foo = node->get_parameter("foo").as_string();
return 0;
} With:
Which I think is an reasonably clear error, especially if you include the backtrace from gdb:
Why do you think they are contradictory? (honestly we should document/clarify their difference and interactions if the change log and/or docstrings don't) From my perspective they do two separate things. One changes the behavior of get/set parameters and the other automatically declares any parameters given to the node, even if the user doesn't use them.
That's definitely what I recommend. I think it's a _good thing_™ if users state in their gazebo plugin which parameters they are expecting to have, and in the future hopefully set some constraints on those parameters, like ranges or choices or something (for which In most cases you should be able to replace a single
You're right, after fixing this bug, however, I think that statement about "getting the old behavior" should be true.
Yeah, I think that is a good name. As I said, I used "initial parameter values" in the docs a lot, but somehow that didn't translate to the option. I also thought about
I'm hoping that once we address this bug, Automatically declaring them would only change whether or not
Yeah, it wasn't meant to be that sharp of a corner, the idea was that changes to code for this break would either be "set |
Yup, I think it's this one. I compiled those example tests in
Think of it as having 2 modes: declared and undeclared. When I'm in "undeclared parameters mode", I wouldn't expect needing to set a variable that declares parameters.
I think it would be great to add this to the documentation of As for getting the list of parameters, I'll comment on your PR with some more specific questions. |
I closed #739 because I think you guys are right (and I was wrong) about the interaction between I think I completely missed this use case (where you #include <string>
#include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.allow_undeclared_parameters(true);
options.initial_parameters({{"bar", "baz"}});
auto node = std::make_shared<rclcpp::Node>("test_node", "", options);
std::string foo = node->get_parameter("foo").as_string();
// ^ fails without automatically_declare_initial_parameters = true
return 0;
} The more common existing case I ran into was something like this: #include <string>
#include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.allow_undeclared_parameters(true);
options.initial_parameters({{"foo", "baz"}});
auto node = std::make_shared<rclcpp::Node>("test_node", "", options);
node->set_parameter({"foo", "bar"});
std::string foo = node->get_parameter("foo").as_string(); // foo would be "baz"
return 0;
} Which does work, because So, if we want to fix this without any code changes we'd need to update the migration docs to say:
Otherwise the first case I described above will not work as it did in Crystal. I see this doc change as the minimum we should do, unless we opt to do more (which I'll talk about below). Additionally, or instead of the doc change, we could change it so that when I'm still not convinced of this, mostly because now I see that we would need to change Another option is that we could remove At the same time, it's clear that some of our option names (e.g. I'm going to propose some additional options in a follow up comment, maybe even make pr's for each option and only merge one of the two. |
I opened two pr's to the dashing migration notes, each describing one of the two ways I think we could go:
Please comment on those or here or propose yet other options as changes to those migration notes so we can discuss them. Separately from that, I'd like to touch up some of the option names and terminology we use. But I'll propose those separately. |
Looks good. It's clear and minimal.
I see only three valuable cases, and one case which shouldn't be recommended. As I commented above, I don't know who would want to use
If we don't want to allow the combination I commented above, I wouldn't go in this way.
For me, it sounds a little contradictory too. But if the documentation is clear enough, I think it's ok. For me, ignoring
But that's a bigger change ... |
What about the forth case, allow undeclared parameters but do not automatically declare initial parameters? |
This is the use case for parameters, the user says there's a parameter "foo" with a value "bar" unless otherwise overridden (by the |
I would not, for instance there could be a use case for a "parameter server" which allows external nodes to set any parameter they want (allow undeclared parameters), but specifically wants to be immune to parameters passed on the command line (maybe it shares a process with another node and the user is prone to pass initial parameter values that don't have a node name associated with them, so called "global initial parameter values"). Either way, I see no reason to prevent someone from having this behavior. It's not conceptually inconsistent, allowing it doesn't break anything. It's already part of a some niche settings that 99% of users will/should never use after Dashing. |
Personally, I think the current implementation is sufficient, it allows for all combinations of behaviors we've been able to imagine, can emulate the old behavior (even if I didn't document it properly the first time), and deals with just bool values which is simpler than the multi-state options. But I'm obviously biased. Still looking for a recommendation (or for consensus) on how to move forward. |
Considering that the four combinations of
It's not completely "immune", because the first time it's set, it will get the default value passed from the command line (as the example above). I don't know if that behavior would be good in that example. |
Ok, I'll close the other pr, fixup the comments on ros2/ros2_documentation#235 and merge that. I'll also open a pr to update the name of
That's a fair point, I was specifically thinking immune to have parameters intended for another node implicitly declared on it. But I could see that perhaps we need another parameter which makes declare/set ignore initial parameter values, if we want to give complete control over the behavior, but we could do that later as it would be a completely new option. |
Just caught up with the whole discussion, thank you for offering two alternatives.
The more I look into this the less I understand it 🙃 So I feel like there are way more use cases being covered than I currently understand. I came into this from the standpoint of someone who's just looking at the |
I think I finally understood this comment. When declaring a parameter, the override is taken. And setting an undeclared parameter behaves like declaring. But even though I now understand the underlying implementation, I don't think it makes sense for the end user to ever have a situation where |
That's a fair point. Doing something different presents two problems, though, in my opinion:
That being said, maybe we should change it... Definitely worth discussing with the rest of the team. Maybe not for Dashing though, and that presents a different issue is how to change it in the future without breaking anything... Further more, I realize now that there's now way to declare a parameter and not be influenced by the overrides (like an "non-overridable parameter"?), which is something we may want to have in the future (just a new option to In which case we could change the first set when allow undeclared is enable to use that. That would be a behavior change, but maybe worth doing. Or maybe we expose the same option in set parameter (ignore overrides or similar). Not sure. |
Addressed by renaming the parameter and updating the documentation. |
* Add fault injection macros and unit tests to rcl_action Signed-off-by: Stephen Brawner <brawner@gmail.com> * Addressing feedback Signed-off-by: Stephen Brawner <brawner@gmail.com> * PR Fixup Signed-off-by: Stephen Brawner <brawner@gmail.com> * PR Fixup Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Emerson Knapp <eknapp@amazon.com>
Bug report
Required Info:
Steps to reproduce issue
Setting to accept undeclared parameters and passing initial parameters:
Throws this:
On the other hand, setting the parameters after construction is happy:
Expected behavior
No throw on both cases
Actual behavior
Throw on first case
The text was updated successfully, but these errors were encountered: