I'm trying to remediate some Coverity findings on tainted values due to the use of atoi
and atof
. I switched to an istringstream
, but its not producing expected results for bases other than 10.
If I switch to base 16, enter 0xa and avoid the iss.ignore(2);
, then the result is 0:
$ ./tt.exe 0xa
X: 0
If I switch to base 16, enter 0xa and utilize the iss.ignore(2);
, then the result is an exception:
$ ./tt.exe 0xa
'0xa' is not a value
I visited CPP Reference on istringstream as recommended by @πάντα, but it does not discuss a limitation in this context.
Any ideas what I am doing wrong? Or, how can I get this to work as expected?
$ cat tt.cxx
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdexcept>
using namespace std;
template <class T>
T StringToValue(const std::string& str) {
std::istringstream iss(str);
T value;
if (str.length() >= 2) {
if (str[0] == '0' && (str[1] =='X' || str[1] =='x'))
{
iss.setf(std::ios_base::hex);
iss.ignore(2);
}
}
iss >> value;
if (iss.fail())
throw runtime_error("'" + str +"' is not a value");
return value;
}
int main(int argc, char* argv[])
{
try
{
int x = StringToValue<int>(argc >= 2 ? argv[1] : "ZZZ...");
cout << "X: " << x << endl;
}
catch(const runtime_error& ex)
{
cerr << ex.what() << endl;
return 1;
}
return 0;
}
You're completely overthinking this. Reading a value in hexadecimal notation is easy.
#include <sstream>
#include <iomanip>
#include <cassert>
int main()
{
{
int x = 0;
std::istringstream is("0xa");
is >> std::hex >> x;
assert(x == 10);
}
{
int x = 0;
std::istringstream is("a");
is >> std::hex >> x;
assert(x == 10);
}
}
Regardless, I can identify two problems with your code.
std::ios_base::setf
You're replacing the stream's entire flagset with only std::ios_base::hex
. The flags are not just for numeric bases. To do that, you need to mask out everything else to prevent unsetting other, unrelated, but necessary flags (I don't know what they are):
iss.setf(std::ios_base::hex, std::ios::basefield);
This is why iss >> std::hex
is so much easier.
It's also why you should have constructed a minimal testcase consisting of nothing but a test of iss.setf
before posting!
The case where input is just "a" skips that statement entirely, as it is conditional on the first two characters being "0x".
Move it to just before the iss >> value
, I would.
You can see your fixed code here but, as explored at the start of my answer, the whole switching-on-leading-"0x"
thing is unnecessary, so most of your code can just be removed.