1

I do not understand why the following code is returning *end=='\0\'?

bool isValue(const string &token,int &output_value) //czy string jest wartoscia
{
  char *end = 0;
  errno=0;
  output_value = strtol(token.c_str(),&end,10); //converts string to int
  if (errno!=0) return false;
  return *end=='\0';
}

EDIT: And stupid question but I don't know why there is

bool isValue(const string &token,int &output_value)

instead of

bool isValue(string &token,int &output_value)

and

bool isValue(string token,int output_value)
Yoda
  • 17,363
  • 67
  • 204
  • 344
  • @op Out of curiosity - in the original version of the question, was `errno` declared as a local variable? – Luchian Grigore Aug 21 '12 at 15:01
  • Unfortunately, `strtol` is totally broken. Don’t use it! In particular, it’s not required that `errno` is set if the conversion was unsuccessful because a non-numerical value was passed to it (e.g. `"xyz"`). Luckily, some implementations still set `errno` under these circumstances but it’s not required, the function could just fail silently. – Konrad Rudolph Aug 21 '12 at 15:06
  • @KonradRudolph Not only is it not required, it's forbidden (at least in Posix). However, in such cases, `strtol` is required to set `end` to the address passed into it, even if internally, it has skipped some leading space, etc. So it's possible to detect this condition as well. – James Kanze Aug 21 '12 at 15:15
  • @James True. But libstdc++ (glibc?) luckily ignores this and does the sensible thing. – Konrad Rudolph Aug 21 '12 at 15:18
  • Not answering your specific question, but [How to parse a string to an int in C++?](http://stackoverflow.com/q/194465/237483) gives good suggestion how you can do it. – Christian Ammer Aug 21 '12 at 15:23
  • @KonradRudolph Does it? That may be where I mislearned it, then. I thought like you until a couple of weeks ago, when a colleague pointed it out as an error in my code, and I looked it up in the standard. What the standard requires isn't sensible (I think we agree there), **but**... once the standard has spoken, for an implementation to do anything else is simply wrong (and standards do often require things that aren't sensible, witness `gets`). – James Kanze Aug 21 '12 at 15:53
  • @James [This example code](http://ideone.com/FrZNX) prints errno = 22 on my computer (OS X 10.7, g++ 4.7, no idea which glibc version). – Konrad Rudolph Aug 21 '12 at 15:57
  • @KonradRudolph Interesting. On Linux (but I'm not sure what version of libc I have either), I get `errno == 0` for all of `"1"` (normal), `"1a"`, `"x"`, `""` and `" "`. Also with VC++ (2010). I don't like it, but it's what the C (and Posix) standards require. – James Kanze Aug 21 '12 at 17:05

4 Answers4

3

Would it be wrong to suggest the use of stringstream?

#include <sstream>
#include <string>

using namespace std;

bool isValue(const string &input, int &output)
{
    stringstream ss;
    ss << input;
    return(ss >> output)
}
DuncanACoulter
  • 2,095
  • 2
  • 25
  • 38
  • 2
    No. But it’s wrong to suggest `if (x) return true; else return false;` ;-) – Konrad Rudolph Aug 21 '12 at 15:19
  • @KonradRudolph Good point: There was a mismatch between what I was suggesting and what I was doing. – DuncanACoulter Aug 21 '12 at 15:23
  • 2
    [This top rated and accepted answer](http://stackoverflow.com/a/6154614/237483) is suggesting `strtol` instead of `stringstream`. With `stringstream` you have problems with strings like `1234abc`. – Christian Ammer Aug 21 '12 at 15:30
  • @ChristianAmmer it can be needed, but can not... So, usage of strtol instead of stringstream should have real reasons. – ForEveR Aug 21 '12 at 15:31
  • @ChristianAmmer That was very informative. I'm glad I phrased my answer the way that I did. – DuncanACoulter Aug 21 '12 at 15:34
  • @Christian You can adapt for that but checking for `eof` on the string stream (see `boost::lexical_cast`). `strtol` is just so brittle (se discussion in other comments) that I wouldn’t suggest ever using it. – Konrad Rudolph Aug 21 '12 at 15:59
2

strtol may set global variable errno to some code. http://cplusplus.com/reference/clibrary/cstdlib/strtol/.

If the correct value is out of the range of representable values, LONG_MAX or LONG_MIN is returned, and the global variable errno is set to ERANGE.

Your function returns true if end points on '\0' (end of string) and false otherwise.

Finally, a pointer to the first character following the integer representation in str is stored in the object pointed by endptr

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • The second is only true if `strtol` manages to convert something. If the string doesn't contain any digits, `strtol` sets `end` to it's argument and returns 0 (without changing `errno`). – James Kanze Aug 21 '12 at 15:14
1

errno is an old C hack. It's a global variable (today thread local). strtol sets it to a non-zero value if there is a (detected) error (and unchanged if there is no error, or if its original value wasn't 0—it's designed so that you can enchain a number of calls, and only check at the end).

Note that the code in question is wrong. (I know because I recently made the same error. The semantics of strtol in case of error are strange, to put it mildly.) You need something like:

bool
intValue( std::string const& token, int& toReturn )
{
    char* end = NULL;
    char const* s = token.c_str();
    errno = 0;
    long results = strtol( s, &end, 10 );
    bool error = errno != 0
        || end != s
        || *end == '\0'
        || results <= std::numeric_limits<int>::max()
        || results >= std::numeric_limits<int>::min();
    if ( !error ) {
        toReturn = results;
    }
    return error;
}

Note that 1) you have to ensure that end != s; and 2) you have to range check if the results are to be written to an int. WRT the first, the specification of strtol says that if it doesn't find any characters to convert (i.e. the string doesn't contain any digits), it returns 0, sets end to the beginning of the string, and doesn't modify errno.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

The errno part is probably legacy code. It could have been used at some point, but the code that used it could have been removed, but errno forgotten. You're right, it does nothing and will probably be optimized out. <--- thought errno was a local variable (stealth edit?)

end is

a pointer to the first character following the integer representation in str is stored in the object pointed by endptr.

so basically the condition checks whether there were any other characters in the string after the number.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625