yarp::os::Searchable::check(const ConstString & key, const Value & fallback, const ConstString & comment = "" ) const cannot parse number-only input to string

Created on 29 Dec 2017  ยท  20Comments  ยท  Source: robotology/yarp

Hello,
when I use the Resource Finder (RF) to check the value of a command line option, the RF cannot parse an input string (using .asString()) if it is composed of numbers only. (i.e. myfile.exe --option 123 does not parse "123" as string using .asString()).

The problem is not caused by the parsing of yarp::os::Value to std::string or yarp::os::ConstString, see the code below.

#include <iostream>
#include <yarp/os/Network.h> // for yarp network
#include <yarp/os/ResourceFinder.h>

int main(int argc, char * argv[])
{
    // Definition and configuration or ResourceFinder
    yarp::os::ResourceFinder &rf = yarp::os::ResourceFinder::getResourceFinderSingleton();
    rf.configure(argc, argv);

    // Getting a string from command line option --name
    yarp::os::ConstString name = rf.check("name", yarp::os::Value("default-name")).asString();
    std::cout << "The name is: " << name << std::endl;


    // Parsing and printing yarp::os::Value to ConstString and std::string
    yarp::os::Value value = yarp::os::Value("123");
    yarp::os::ConstString const_string = value.asString();
    std::string std_string = value.asString();

    std::cout << "The ConstString value is : " << const_string << std::endl;
    std::cout << "The std::string value is : " << std_string << std::endl;

    return 0;
}

when I run the command myfile.exe --name 123
I get the following output:

The name is:
The ConstString value is : 123
The std::string value is : 123
Library - YARP_os

Most helpful comment

If it is intentional, then should the same behavior be implemented in the code below?
yarp::os::Value value = yarp::os::Value("123");
yarp::os::ConstString const_string = value.asString();
std::string std_string = value.asString();

That is not a fair comparison, because adding the quotes "" you are forcing it to be a string right _before_ calling Value.
The correct comparison is
yarp::os::Value value = yarp::os::Value(123); without quotes. This is handled the same way.

The point is that there is no equivalent to
yarp::os::Value value = yarp::os::Value("123"); from commad line.

When data are read from command line, they are always a string, so 123 and "123" are actually both a char[] like in main(int argc, char *argv[]).
This makes it impossible to distinguish between the _intended_ usage two.
Everything is a string, so when parsing command line a choise has to be made about the representation to use. _I think the underlying assumption is that when the user write a number, it means a number_, so it is treated accordingly, which is reasonable to me.

If you need to get the string out of it, you can call the "toString()" method. It may be not so straightforward in your case, but I think that's is the correct way to proceed.

All 20 comments

Hi @miccol,
ResourceFinder::check(...) checks if there exists a property of the given name.

See documentation here:
http://www.yarp.it/classyarp_1_1os_1_1ResourceFinder.html#afd52aea1d054524d8c4ebefc458978b4

Sorry I didn't see the second argument in check..
In my opinion I don't like that calling the same function with one, or more parameter you have different behaviour and output.
check() should check that for that key the value exists, not return the value, there is find() for that.
I find this thing very misleading, but maybe it is a personal taste.

BTW I will check why it can't parse number input, thanks for reporting!

I totally agree! the name is misleading and I prefer find too. However, I cannot find a way to define a default value using find directly (as done for check).

check is the service devoted to deal with options that are present or not provided.
In case they're not provided, you can ask for default values instead, using the second parameter.

For me, check is a better solution than find, if you consider that you cannot find an option that is not there ๐Ÿ˜‰

Just a reminder for me, also happening with '123' and "123"

@miccol if you replace this

yarp::os::ConstString name = rf.check("name", yarp::os::Value("default-name")).asString();

with this

yarp::os::ConstString name = rf.check("name", yarp::os::Value("default-name")).toString();

it works

@miccol if you replace this

yarp::os::ConstString name = rf.check("name", yarp::os::Value("default-name")).asString();

with this

yarp::os::ConstString name = rf.check("name", yarp::os::Value("default-name")).toString();

it works

I'm a bit confused by this behaviour.
The question now is: why, _in this particular working condition_ (e.g. input: --name 123), asString and toString provide two different results?

  • toString calls for the string representation of the Value, so it sort of forces to transform an object into a string. Any content can be dumped as a string, hence it will always work, regardless whether the value is a string, an integer, a double, a bool, or a list.
  • asString asks to return a Value as a string, but if the original value is not a string then I believe the outcome will be kind of unpredictable, or at least different from what expected.
  • isString returns true if the value is actually a string, false otherwise.

--name 123 is interpreted as an integer, I think, thus asString doesn't work.

Thereby, try out --name "123".

"" '' seems to be ignored:

  • --name "123"--> my name is
  • --name '123'--> my name is
  • --name 'ciao'--> my name is ciao

Probably in rf.configure(argc, argv)

When I aim to pass on a list as command line option, I usually do:

--name "(item_0 item_1 ...)"

and calling from the code

Bottle *list=rf.find("name").asList();

works. Therefore, somehow there must be cases where "..." are well interpreted inside rf.configure().

That said, however, out of curiosity, why would one pass on a sheer integer as a string?

When you call the is* methods, that's the outcome:

| | input | isInt() | isString() | isList() |
| ---: | :---: | :---: | :---: | :---: |
| 1 | abc | โŒ | โœ”๏ธ | โŒ |
| 2 | "abc" | โŒ | โœ”๏ธ | โŒ |
| 3 | 123 | โœ”๏ธ | โŒ | โŒ |
| ๐Ÿ‘‰ 4 | "123" | โœ”๏ธ | โŒ | โŒ |
| 5 | "a b 1" | โŒ | โœ”๏ธ | โŒ |
| ๐Ÿ‘‰ 6 | "(a b 1)" | โŒ | โŒ | โœ”๏ธ |

Thereby, it seems that the ResourceFinder makes some assumptions while parsing the input options. As a result, this behavior isn't necessarily to be considered buggy.

In particular, line 6 is the only viable (hence required) solution to pass on a list through the command line. Thus, I tend to think of line 4 as a natural extension.

So do we all agree that it is not a bug but an intended implementation? Shall we close this issue?

If it is intentional, then should the same behavior be implemented in the code below?

    yarp::os::Value value = yarp::os::Value("123");
    yarp::os::ConstString const_string = value.asString();
    std::string std_string = value.asString();

My point was only to highlight the fact that there must something else going on inside ResourceFinder with respect to Value since it has to parse a more complex input that has to do with the command line options.

However, let me add another twist to the mystery.

Look at here: https://github.com/robotology/icub-main/blob/master/src/tools/controlBoardDumper/main.cpp#L935-L940

rf.setDefault("part","head");
rf.setDefault("robot","icub");
rf.setDefault("rate","500");
rf.setDefault("joints","(0)");
rf.setDefault("dataToDump","(getEncoders getEncoderSpeeds getEncoderAccelerations getPositionErrors getOutputs getCurrents getTorques getTorqueErrors)");    
rf.configure(argc,argv);

Apparently, the method setDefault() correctly works out a string containing a plain number.
Can anyone extend the test with the use of setDefault()?

@miccol I would change the way RF operates rather than changing the behavior of Value.

Even if we're dealing with a quite significant corner case here, RF can return "123", "123.4" as strings rather than as numbers.

I would better change the user code in order to make it foresee inputs as numbers and then change them internally to strings by means of the C++11 routine to_string().

If it is intentional, then should the same behavior be implemented in the code below?
yarp::os::Value value = yarp::os::Value("123");
yarp::os::ConstString const_string = value.asString();
std::string std_string = value.asString();

That is not a fair comparison, because adding the quotes "" you are forcing it to be a string right _before_ calling Value.
The correct comparison is
yarp::os::Value value = yarp::os::Value(123); without quotes. This is handled the same way.

The point is that there is no equivalent to
yarp::os::Value value = yarp::os::Value("123"); from commad line.

When data are read from command line, they are always a string, so 123 and "123" are actually both a char[] like in main(int argc, char *argv[]).
This makes it impossible to distinguish between the _intended_ usage two.
Everything is a string, so when parsing command line a choise has to be made about the representation to use. _I think the underlying assumption is that when the user write a number, it means a number_, so it is treated accordingly, which is reasonable to me.

If you need to get the string out of it, you can call the "toString()" method. It may be not so straightforward in your case, but I think that's is the correct way to proceed.

I think that @barbalberto got the point, it has been a very useful discussion but for me we can close this issue, do you agree?

FIne for me. Thanks @barbalberto !

Was this page helpful?
0 / 5 - 0 ratings