rclcpp: Can't set initial parameters when allowing undeclared

Bug report

Required Info:

  • Operating System:
    • Ubunto Bionic
  • Installation type:
    • dashing sources
  • Version or commit hash:
    • master

Steps to reproduce issue

Setting to accept undeclared parameters and passing initial parameters:

  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);
  node->get_parameter(param);  // throws

Throws this:

12: terminate called after throwing an instance of 'rclcpp::ParameterTypeException'            
12:   what():  expected [string] got [not set]  

On the other hand, setting the parameters after construction is happy:

  rclcpp::NodeOptions node_options;
  node_options.allow_undeclared_parameters(true);
  auto node = std::make_shared<Node>("name", "ns", node_options);
  node->set_parameters(initial_parameters);
  node->get_parameter(param); // happy

Expected behavior

No throw on both cases

Actual behavior

Throw on first case

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 24 (24 by maintainers)

Commits related to this issue

Most upvoted comments

I cannot reproduce the issue in the original post, get_parameter doesn’t throw for me (my demo below).


Either way, I’d recommend these as the “golden rules” for parameters in Dashing:

  • Always at least use declare_parameter(s), and use it before using any has/get/set parameter methods.
  • Avoid using allow_undeclared_parameters and automatically_declare_initial_parameters except in exceptional cases, you most likely should not be using them.

I think the fundamental misunderstanding is that NodeOptions::initial_parameter is not intended to seed the node with parameters, it is actually only for overriding parameter defaults (same goes for the YAML file). I think this is a reasonable thing to be confused about, given the name, so perhaps we should change it. Most places I refer to this as the “initial parameter values” rather than “initial parameters”.

The automatically_declare_initial_parameters is essentially a helper which uses those overrides to declare the parameters for you.

The documentation says

That means when you, the user, set a value for a parameter (using set_parameter(s)) it gets automatically declared (as if you had declare_parameter), but it is undeclared until you set it. If you only ever use initial parameter values and get, then the parameter will never be declared.

So I would expect that to also happen to undeclared parameters set through the constructor without needing to also set automatically_declare_initial_parameters.

I think you’re misunderstanding allow_undeclared_parameters, it only avoids two things. First, throwing when you ‘get’ a parameter that has not been declared explicitly, and instead returns a rclcpp::Parameter with the type set to PARAMETER_NOT_SET (essentially like the None type in Python). Second, it also will avoid throwing when you use set_parameter(s)*, instead it implicitly declares the parameter and then sets it.

It does not allow for parameters to be declared automatically just because they’re passed as initial parameters or in the YAML file.

Maybe, allow_undeclared_parameters should set automatically_declare_initial_parameters to true

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;
}