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

Can't pass in a numerical string as arg param to C++ node #1339

Open
lucasw opened this issue Mar 3, 2018 · 9 comments
Open

Can't pass in a numerical string as arg param to C++ node #1339

lucasw opened this issue Mar 3, 2018 · 9 comments

Comments

@lucasw
Copy link
Contributor

lucasw commented Mar 3, 2018

If a numerical string like "4235" or "57.23" is set onto the parameter server as a command line argument to a C++ node then it is turned into type int or float on the parameter server and a getParam of type string will not get it.

#include <ros/ros.h>

int main(int argc, char** argv)
{
  ros::init(argc, argv, "string_param_test");
  ros::NodeHandle nh("~");

  // this comes up blank if str_param was passed in as argument, or set
  // rosparam set /string_param_test/str_param "98432"
  // the type is int so a string getParam fails.
  std::string test = "";
  nh.getParam("str_param", test);
  std::cout << test << "\n";

  // this works as desired
  nh.setParam("str_param2", "12345");
  nh.getParam("str_param2", test);
  std::cout << test << "\n";

  return 0;
}
$ rosrun param_test string_param_test _str_param:="98432"

12345
$ rosparam get /string_param_test/str_param2
'12345'

It looks like the root of the issue is that setting a parameter with quotes that is a valid int or float turns into an int or float:

$ rosparam set /string_param_test/str_param '8945'
$ rosparam get /string_param_test/str_param 
8945

I'm using kinetic on Ubuntu 16.04.

I also tried this with kinetic-devel, though got a ‘ros_steadytime’ is not a member of ‘ros’ error (even when bringing in the latest roscpp_core) so went back to d4788bc .

(I experimented with adding unit tests here but I'm not sure how to quickly build and test them in a catkin tools build environment - it takes a long time to build and run many tests of test_roscpp, I can't selectively just do a few. How to build just ./devel/lib/test_roscpp/test_roscpp-params and not run it or anything else? Extract the build line out of a verbose build/run of everything?)

@lucasw
Copy link
Contributor Author

lucasw commented Mar 4, 2018

Double quotes with single inner quotes works for rosparam set, the outer quotes are stripped of, and the inner quotes clue rosparam that the type is string:

rosparam set /test "'98432'"
rosparam get /test 
'98432'

But using that as an arg to a C++ node and the string is '98432', with the single quotes as characters in the string, but in a python node the quotes are stripped and the type is correctly determined to be a string.

rosparam get on a string with single quotes yields

'''98432'''
#!/usr/bin/env python

import rospy

rospy.init_node("string_param_test")
val = rospy.get_param("~str_param", None)
print val, type(val)

$ rosparam set /string_param_test/str_param "'4325'"
$ rosrun param_test string_param_test.py
4325 <type 'str'>

$ rosrun param_test string_param_test.py _str_param:="'4325'"
4325 <type 'str'>

$ rosrun param_test string_param_test.py _str_param:='"4325"'
4325 <type 'str'>
rosrun param_test string_param_test.py _str_param:="4325"
4325 <type 'int'>

@lucasw
Copy link
Contributor Author

lucasw commented Mar 4, 2018

Double + single quotes doesn't work for roslaunch <param> tags (regardless if the node using the param is C++ or python), the inner single quotes become part of the string as with args to a C++ node.

(type="str" works fine but that isn't available outside of roslaunch)

@lucasw lucasw changed the title numerical string param is turned into int or float even with quotes around it using args or rosparam set Can't pass in a numerical string as arg param to C++ node Mar 4, 2018
lucasw added a commit to lucasw/ros_comm that referenced this issue Mar 4, 2018
lucasw added a commit to lucasw/ros_comm that referenced this issue Mar 4, 2018
…, so numerical strings get stored like they do through rosparam set and rospy init ros#1339
@gavanderhoorn
Copy link
Contributor

I don't see it in your example invocations, but is '"12345"' also problematic? The ' should prevent the shell from evaluating the argument, so I would expect the " to be preserved.

@lucasw
Copy link
Contributor Author

lucasw commented Mar 9, 2018

'"12345"' results in 12345 of type string when given to rosparam set or rospy arg so my pr makes C++ and roslaunch args match that.

Would the argument for going the other way and making '"12345"' preserve the inner quotes in all cases be that there isn't otherwise a good way to set a string with quotes around it?

With some further experiments with quotes within strings it looks like there is an additional issue:

$ rosparam set /foo "'foo' blah" 
Traceback (most recent call last):
  File "/opt/ros/kinetic/bin/rosparam", line 35, in <module>
    rosparam.yamlmain()
  File "/opt/ros/kinetic/lib/python2.7/dist-packages/rosparam/__init__.py", line 626, in yamlmain
    _rosparam_cmd_set_load(command, argv)
  File "/opt/ros/kinetic/lib/python2.7/dist-packages/rosparam/__init__.py", line 543, in _rosparam_cmd_set_load
    set_param(name, arg2, verbose=options.verbose)
  File "/opt/ros/kinetic/lib/python2.7/dist-packages/rosparam/__init__.py", line 375, in set_param
    set_param_raw(param, yaml.load(value), verbose=verbose)
  File "/usr/lib/python2.7/dist-packages/yaml/__init__.py", line 71, in load
    return loader.get_single_data()
  File "/usr/lib/python2.7/dist-packages/yaml/constructor.py", line 37, in get_single_data
    node = self.get_single_node()
  File "/usr/lib/python2.7/dist-packages/yaml/composer.py", line 39, in get_single_node
    if not self.check_event(StreamEndEvent):
  File "/usr/lib/python2.7/dist-packages/yaml/parser.py", line 98, in check_event
    self.current_event = self.state()
  File "/usr/lib/python2.7/dist-packages/yaml/parser.py", line 174, in parse_document_start
    self.peek_token().start_mark)
yaml.parser.ParserError: expected '<document start>', but found '<scalar>'
  in "<string>", line 1, column 7:
    'foo' blah

rosparam set /foo '"foo" blah' is the same, but putting quotes later than the first non-space character works:

$ rosparam set /foo 'blah "foo"' && rosparam get /foo
blah "foo"

So there is some code (in yaml parser not rospy?) that is looking for a leading quote regardless of a matching ending quote and then misinterprets and throws.

Escaping the space and no outer quotes succeeds but loses the quotes:

rosparam set /foo '1234'\ blah  && rosparam get /foo
1234 blah

rosparam get could be losing quotes that rosparam set preserved, I'll have to look at that later.

@gavanderhoorn
Copy link
Contributor

gavanderhoorn commented Mar 9, 2018

Strings in yaml don't necessarily need quotes. That is probably why you don't see them in rosparam get output.

Not seeing quotes also doesn't necessarily mean it's not a string.

@lucasw
Copy link
Contributor Author

lucasw commented Mar 9, 2018

Not seeing quotes also doesn't necessarily mean it's not a string.

I've noticed that rosparam prints quotes when it it is ambiguous: 1234 type string prints '1234', but 1234x is just plain 1234x.

-p pretty print drops the quotes.

(It would be convenient if -v printed the type definitively for what is in the rosparam server, I could add that later)

@flixr
Copy link
Contributor

flixr commented Aug 3, 2018

We are encountering the same problem...

@VictorLamoine
Copy link
Contributor

VictorLamoine commented Dec 17, 2019

For those looking for a work-around:

std::string serial_number;
if (!nh->getParam("connect_serial_number", serial_number))
{
  // When using rosrun the serial number is interpreted as an int and
  // can't be forced into a string. See https://github.com/ros/ros_comm/issues/1339
  int serial_number_int;
  if (nh->getParam("connect_serial_number", serial_number_int))
    serial_number = std::to_string(serial_number_int);
}

First: try to get the parameter as a string.
If it fails: try to get the parameter as an int and convert it to a string.
With this it's pretty easy to support integers/floats.

Then you can run the node and pass the argument as you would expect:

rosrun my_package my_node _connect_serial_number:=190345

@flixr
Copy link
Contributor

flixr commented Dec 18, 2019

yeah, but that workaround has limitations... e.g. it won't work for strings with leading zeros..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants