Skip to content
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

declare_parameter<T>(param_name) fails #1691

Open
m2-farzan opened this issue Jun 9, 2021 · 8 comments
Open

declare_parameter<T>(param_name) fails #1691

m2-farzan opened this issue Jun 9, 2021 · 8 comments
Assignees
Labels
bug Something isn't working

Comments

@m2-farzan
Copy link
Contributor

Bug report

Required Info:

  • Operating System:
    • Archlinux @ June, 9 [GCC 11.1.0]
  • Installation type:
    • From source
  • Version or commit hash:
  • DDS implementation:
    • rmw_fastrtps_cpp

Steps to reproduce issue

Try to run the following code:

#include "rclcpp/rclcpp.hpp"
#include <string>

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::Node mynode("name");
    mynode.declare_parameter<std::string>("myparam");
    rclcpp::shutdown();
}

Expected behavior

Run and exit successfully.

Actual behavior

Compiles but in runtime exits due to an exception. Full output:

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

Additional information

This call fails:

mynode.declare_parameter<std::string>("myparam");

(This is not specific to std::string)

Here's my understanding of why this happens: Under the hood, rclcpp declares an uninitialized parameter and tries to return it as a typed object (code here). However, it seems that rclcpp keeps track of uninitialized parameters by assigning not set type to them (code here), even if they are typed. So this boils down to contradictory typing (string type implied by template argument, not set type implied by the node being uninitialized...)

Interestingly the mynode.declare_parameter<T>(param_name) function call is actually included in the tests (code here) and they are passing without errors. The reason that the error doesn't manifest there is that in the same test file, a default value is defined for the parameter (code here). This means the parameter gets initialized (and therefore loses the not set type) before mynode.declare_parameter<T>(param_name) finishes.

fujitatomoya added a commit to fujitatomoya/ros2_test_prover that referenced this issue Jun 10, 2021
  ros2/rclcpp#1691

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
@ivanpauno ivanpauno added the bug Something isn't working label Jun 11, 2021
@ivanpauno ivanpauno self-assigned this Jun 11, 2021
@ivanpauno
Copy link
Member

This one is unfortunate, but I don't think we can solve it.
The error message should be clearer though.

When we decided to make parameters "strongly typed" by default (#1522), we didn't originally support to declare them uninitialized.
You either needed to pass a default value or the user must provide an override of the correct type (e.g. <param_name>:=<param_value>) when running the node.

We later relaxed that requirement (#1681), and it is now possible to do the following:

rclcpp::ParameterValue param = node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER);

The return type here is a rclcpp::ParameterValue, which will be of type int64_t if the parameter is initialized or of type "PARAMETER_NOT_SET" when not.

The templated version returns directly the real parameter type and not an rclcpp::ParameterValue, so it's impossible to achieve similar behavior:

// What do we return if the parameter is uninitialized?
int64_t param = node->declare_parameter<int64_t>("integer_override_not_given");

So I don't think we can fix this for the templated version, and the non-template method should be used when you want to allow the parameter to be uninitialized.


TBH, I completely forgot about the templated overload when we introduced #1681.
I think that at least the error message should be better (and maybe also the docs).

@jacobperron what do you think?

@m2-farzan
Copy link
Contributor Author

IMHO the clean way would be to avoid using 'type' to indicate whether a parameter is initialized or not. It makes sense to have a separate initialized flag in ParameterValue for this purpose. This is an addition but the logical correctness will make everything smoother.

Just my two cents...

@ivanpauno
Copy link
Member

IMHO the clean way would be to avoid using 'type' to indicate whether a parameter is initialized or not

"type" doesn't indicate if a parameter is initialized or not.
@m2-farzan I don't understand what you exactly mean, could you provide a complete example?

@m2-farzan
Copy link
Contributor Author

Sorry, let me put it another way.

You said it:

The return type here is a rclcpp::ParameterValue, which will be of type int64_t if the parameter is initialized or of type "PARAMETER_NOT_SET" when not.

What I'm suggesting here is to return a ParameterValue of type int64_t regardless of whether the parameter is initialized or not. The information about initialized/uninitialized state will be stored in a new boolean member of ParameterValue, potentially called initialized.

@ivanpauno
Copy link
Member

What I'm suggesting here is to return a ParameterValue of type int64_t regardless of whether the parameter is initialized or not. The information about initialized/uninitialized state will be stored in a new boolean member of ParameterValue, potentially called initialized.

Thanks for clarifying!
That seems like a valid approach but I don't think it's worth breaking API as what you want to do is still possible now.

I would just focus in making the error message more clear, but I will wait for @jacobperron input first.

@jacobperron
Copy link
Member

jacobperron commented Jun 17, 2021

I also overlooked the template overload... we could certainly improve the error message.
It doesn't seem like something we can "fix" for Galactic, in order to keep API stability. We could introduce an additional "initialized" state variable, but I'm not sure if it's worth the churn. As @ivanpauno points out, we have a way to declare a parameter without a value. Also, if we changed the signature of the templated overload to return a rclcpp::ParameterValue, we can already check if the parameter is initialized by checking the value's type. For example, the following code works for me:

rclcpp::ParameterValue param = node->declare_parameter("foo", rclcpp::PARAMETER_INTEGER);
if (param.get_type() == rclcpp::PARAMETER_NOT_SET) {
  // Parameter is unset
}

So, I'm not sure if a new "initialized" member adds value.

@jacobperron
Copy link
Member

It might be valuable to support returning a rclcpp::ParameterValue object from the templated method (I haven't thought about how technically feasible it is). I.e.

rclcpp::ParameterValue param = node->declare_parameter<int64_t>("foo");

Then we wouldn't get an exception, and could check if the parameter is set before actually accessing the value.

ayrton04 pushed a commit to cra-ros-pkg/robot_localization that referenced this issue Jun 17, 2021
The templated declare_parameter method needs an override (or default value) since
it returns the actual parameter value, otherwise we get an exception.
To correctly declare a parameter without a default value, we should use a different
signature.

Related upstream issue: ros2/rclcpp#1691

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
@ivanpauno
Copy link
Member

It might be valuable to support returning a rclcpp::ParameterValue object from the templated method (I haven't thought about how technically feasible it is)

It's not possible to have two overloads with the same argument and different return value, so the only way to do that is to change the return value of the current method.

For the moment, I will improve the error message.
We can later decide if we want to modify the templated method signature or not.

emilnovak pushed a commit to EnjoyRobotics/robot_localization that referenced this issue Jul 26, 2021
…a-ros-pkg#675)

The templated declare_parameter method needs an override (or default value) since
it returns the actual parameter value, otherwise we get an exception.
To correctly declare a parameter without a default value, we should use a different
signature.

Related upstream issue: ros2/rclcpp#1691

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
cesar-lopez-mar pushed a commit to nobleo/robot_localization that referenced this issue Sep 14, 2021
…a-ros-pkg#675)

The templated declare_parameter method needs an override (or default value) since
it returns the actual parameter value, otherwise we get an exception.
To correctly declare a parameter without a default value, we should use a different
signature.

Related upstream issue: ros2/rclcpp#1691

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
SteveMacenski pushed a commit to cra-ros-pkg/robot_localization that referenced this issue Sep 15, 2021
* Fix runtime exception thrown when parameter override not provided (#675)

The templated declare_parameter method needs an override (or default value) since
it returns the actual parameter value, otherwise we get an exception.
To correctly declare a parameter without a default value, we should use a different
signature.

Related upstream issue: ros2/rclcpp#1691

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Fix different time sources error (#679)

Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
erikboto added a commit to erikboto/gps_umd that referenced this issue Sep 6, 2023
When trying to run the constructor throws an exception:
"Component constructor threw an exception: Statically typed parameter
'use_gps_time' must be initialized."

This problem is described here:
ros2/rclcpp#1691

Solve this by using the non-template way of declaring the parameters.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants