diff --git a/rclcpp/include/rclcpp/exceptions/exceptions.hpp b/rclcpp/include/rclcpp/exceptions/exceptions.hpp index 27c695c0b6..524ccf73ea 100644 --- a/rclcpp/include/rclcpp/exceptions/exceptions.hpp +++ b/rclcpp/include/rclcpp/exceptions/exceptions.hpp @@ -282,8 +282,8 @@ class ParameterModifiedInCallbackException : public std::runtime_error using std::runtime_error::runtime_error; }; -/// Thrown when a parameter override wasn't provided and one was required. -class NoParameterOverrideProvided : public std::runtime_error +/// Thrown when an uninitialized parameter is accessed. +class ParameterUninitializedException : public std::runtime_error { public: /// Construct an instance. @@ -291,8 +291,8 @@ class NoParameterOverrideProvided : public std::runtime_error * \param[in] name the name of the parameter. * \param[in] message custom exception message. */ - explicit NoParameterOverrideProvided(const std::string & name) - : std::runtime_error("parameter '" + name + "' requires an user provided parameter override") + explicit ParameterUninitializedException(const std::string & name) + : std::runtime_error("parameter '" + name + "' is not initialized") {} }; diff --git a/rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp b/rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp index 1f9e32acbc..3d91943bc5 100644 --- a/rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp +++ b/rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp @@ -349,6 +349,21 @@ __declare_parameter_common( initial_value = &overrides_it->second; } + // If there is no initial value, then skip initialization + if (initial_value->get_type() == rclcpp::PARAMETER_NOT_SET) { + // Add declared parameters to storage (without a value) + parameter_infos[name].descriptor.name = name; + if (parameter_descriptor.dynamic_typing) { + parameter_infos[name].descriptor.type = rclcpp::PARAMETER_NOT_SET; + } else { + parameter_infos[name].descriptor.type = parameter_descriptor.type; + } + parameters_out[name] = parameter_infos.at(name); + rcl_interfaces::msg::SetParametersResult result; + result.successful = true; + return result; + } + // Check with the user's callback to see if the initial value can be set. std::vector parameter_wrappers {rclcpp::Parameter(name, *initial_value)}; // This function also takes care of default vs initial value. @@ -413,14 +428,6 @@ declare_parameter_helper( parameter_descriptor.type = static_cast(type); } - if ( - rclcpp::PARAMETER_NOT_SET == default_value.get_type() && - overrides.find(name) == overrides.end() && - parameter_descriptor.dynamic_typing == false) - { - throw rclcpp::exceptions::NoParameterOverrideProvided(name); - } - rcl_interfaces::msg::ParameterEvent parameter_event; auto result = __declare_parameter_common( name, @@ -806,14 +813,21 @@ NodeParameters::get_parameters(const std::vector & names) const rclcpp::Parameter NodeParameters::get_parameter(const std::string & name) const { - rclcpp::Parameter parameter; + std::lock_guard lock(mutex_); - if (get_parameter(name, parameter)) { - return parameter; + auto param_iter = parameters_.find(name); + if ( + parameters_.end() != param_iter && + (param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET || + param_iter->second.descriptor.dynamic_typing)) + { + return rclcpp::Parameter{name, param_iter->second.value}; } else if (this->allow_undeclared_) { - return parameter; - } else { + return rclcpp::Parameter{}; + } else if (parameters_.end() == param_iter) { throw rclcpp::exceptions::ParameterNotDeclaredException(name); + } else { + throw rclcpp::exceptions::ParameterUninitializedException(name); } } diff --git a/rclcpp/test/rclcpp/test_node.cpp b/rclcpp/test/rclcpp/test_node.cpp index dff05a0667..8d2e8a8410 100644 --- a/rclcpp/test/rclcpp/test_node.cpp +++ b/rclcpp/test/rclcpp/test_node.cpp @@ -335,9 +335,14 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) { rcl_interfaces::msg::ParameterDescriptor descriptor; descriptor.dynamic_typing = true; // no default, no initial + const std::string parameter_name = "parameter"_unq; rclcpp::ParameterValue value = node->declare_parameter( - "parameter"_unq, rclcpp::ParameterValue{}, descriptor); + parameter_name, rclcpp::ParameterValue{}, descriptor); EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET); + // Does not throw if unset before access + EXPECT_EQ( + rclcpp::PARAMETER_NOT_SET, + node->get_parameter(parameter_name).get_parameter_value().get_type()); } { // int default, no initial @@ -2798,9 +2803,20 @@ TEST_F(TestNode, static_and_dynamic_typing) { EXPECT_EQ("hello!", param); } { + auto param = node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER); + EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type()); + // Throws if not set before access EXPECT_THROW( - node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER), - rclcpp::exceptions::NoParameterOverrideProvided); + node->get_parameter("integer_override_not_given"), + rclcpp::exceptions::ParameterUninitializedException); + } + { + auto param = node->declare_parameter("integer_set_after_declare", rclcpp::PARAMETER_INTEGER); + EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type()); + auto result = node->set_parameter(rclcpp::Parameter{"integer_set_after_declare", 44}); + ASSERT_TRUE(result.successful) << result.reason; + auto get_param = node->get_parameter("integer_set_after_declare"); + EXPECT_EQ(44, get_param.as_int()); } { EXPECT_THROW(