简体   繁体   中英

Read uint8_t from std::stringstream as a numeric type

My understanding is that reading a uint8_t from a stringstream is a problem because the stringstream will interpret the uint8_t as a char . I would like to know how I can read a uint8_t from a stringstream as a numeric type. For instance, the following code:

#include <iostream>
#include <sstream>

using namespace std;

int main()
{
    uint8_t ui;
    std::stringstream ss("46");
    ss >> ui;
    cout << unsigned(ui);
    return 0;
}

prints out 52 . I would like it to print out 46 .

EDIT: An alternative would to just read a string from the stringstream and then convert the solution to uint8_t , but this breaks the nice chaining properties. For example, in the actual code I have to write, I often need something like this:

   void foobar(std::istream & istream){
       uint8_t a,b,c;
       istream >> a >> b >> c;
       // TODO...
   }

You can overload the input operator>> for uint8_t , such as:

std::stringstream& operator>>(std::stringstream& str, uint8_t& num) {
   uint16_t temp;
   str >> temp;
   /* constexpr */ auto max = std::numeric_limits<uint8_t>::max();
   num = std::min(temp, (uint16_t)max);
   if (temp > max) str.setstate(std::ios::failbit);
   return str;
}

Live demo: https://wandbox.org/permlink/cVjLXJk11Gigf5QE

To say the truth I am not sure whether such a solution is problem-free. Someone more experienced might clarify.


UPDATE

Note that this solution is not generally applicable to std::basic_istream (as well as it's instance std::istream ), since there is an overloaded operator>> for unsigned char : [istream.extractors] . The behavior will then depend on how uint8_t is implemented.

After much back and forth, the answer seems to be that there is no standard way of doing this. The options are to either read off the uint8_t as either a uint16_t or std::string , and then convert those values to uint8_t :

#include <iostream>
#include <sstream>

using namespace std;

int main()
{
    uint8_t ui;
    uint16_t tmp;
    std::stringstream ss("46");
    ss >> tmp;
    ui = static_cast<uint8_t>(tmp);
    cout << unsigned(ui);
    return 0;
}

However, such a solution disregards range checking. So you will need to implement that yourself if you need it.

Please do not use char or unsigned char (uint8_t) if you want to read in a formatted way. Your example code and its result is an expected behavior.

As we can see from https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt2

template< class Traits >
basic_istream<char,Traits>& operator>>( basic_istream<char,Traits>& st, unsigned char& ch );

This does "Performs character input operations".

52 is an ascii code for '4'. Which means that the stringstream has read only one byte and still ready to read '6'.

So if you want work in the desired way, you should use 2-byte or bigger integer types for sstream::operator>> then cast it to uint8_t - the exact way that you self-answered.

Here's a reference for those overloads. https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM