pdm2011 pdm2011 - 1 month ago 7
C++ Question

Insert from stringstream with implict conversion

I am reading whitespace-delimited single precision values in scientific notation from an ASCII file. There are multiple values per line. I populate a vector via a streamstream; the primitive type of the vector may differ from the type of the data:

// Illustrates typical string from file
std::string string_with_floats("-2.750000e+001 2.750000e+001 3.450000e+001");

// Template parameter is the desired return type
vector<int> data = ReadValues<int>(string_with_floats);

template <class T>
vector<T>& ReadValues(std::string& string_with_data)
{
std::stringstream ss(string_with_data);
std::vector<T> values;
T val;
while(stream >> val)
{
values.push_back(val);
}
return values;
}


The example above results in the vector being populated with just the first value, truncated to -2, presumably because the loop terminates as soon as a non-numeric character is encountered. It works as expected when the incoming string contains int values, even when the template parameter is float.

Is there a way to configure a stringstream to perform an implicit conversion and round to the nearest integer, or do I need to insert into the original primitive type first (float) and perform an explicit cast to T? Ideally, I would like to not have to tell ReadValues about the type of the data in string_with_data - it will always be one of double, float, int, short, or long, and the requested type can also be any of these types.

Thanks.

Answer

If you know the type of the data in string_with_data, it would be simpler: just read one single value of that type and then cast it to the required type before pushing it into the vector:

template <class T>
vector<T>& ReadValues(std::string& string_with_data)
{
    std::stringstream ss(string_with_data);
    std::vector<T> values;
    float val; // use original type not required one
    while(stream >> val)
    {
        values.push_back((T) val);
    }
    return values;
}

If you do not know the original type, it will a little harder, because there is not single type in C++ nor in standard library that is big enough to accept all other types. So you will have to read each data as a string and then decode it into the required type with an auxilliary stringstream:

template <class T>
std::vector<T> ReadValues(std::string& string_with_data)
{
    std::stringstream ss(string_with_data);
    std::vector<T> values;
    std::string str;
    T val;
    while(ss >> str)         // first store the value into a string whatever it is
    {
        std::stringstream(str) >> val;   // then read it as the required type
        values.push_back(val);
    }
    return values;
}

That way you can be sure that:

  • you have not left the trailing part of a float in the stream when reading an int
  • you use the maximum precision allowed by the required type when reading the original string

NOTE : I assume that you accept the default conversions...