5
votes

This:

#include <iostream>
#include <sstream>
#include <inttypes.h>

using namespace std;

int main (void) {
    istringstream iss("123 42");
    int8_t x;
    while (iss >> x) {
        cout << x << endl;
    }
    return 0;
}  

Produces:

1
2
3
4
2

But I want:

123
42

Casting iss >> (int)x (I initially tried this with a char) gives me "error: invalid operands to binary expression ('istringstream' (aka 'basic_istringstream') and 'int')" (clang) or "error: ambiguous overload for ‘operator>>’" (g++).

Is there a way to read the value as a number directly into an 8-bit type, or do I have to use an intermediary store?

5
Note that this output 1 2 3 4 2 2 is still wrong, as 2 is printed twice in the end. And it is because of the wrong loop. You should use loop like while(stream >> item){ ... }.Nawaz
@Nawaz Point taken, corrected.CodeClown42

5 Answers

3
votes

There is no built-in 8-bit type; you're using an alias for signed char and IOStreams will always extract a single ASCII letter when you do formatted input into any kind of char.

So, yes, use an intermediary store, or wrap int8_t in a new class that provides its own overloads for formatted I/O (which I'd consider overkill unless you have strict memory and/or performance requirements).

(Your attempt of iss >> (int)x is very confused; conversions are used on expressions you're about to take the value of, not for lvalues naming objects that you want to set the value of.)

2
votes

You have to use an intermediate type or do the parsing yourself. All char-types (char, signed char and unsigned char) are treated as text elements, not integers. int8_t is probably just a typedef for one of them, which is why your code fails.

Note:

  • The output will suffer from the same issues.
  • Don't use C-style casts, they almost only cause errors.
  • Checking for EOF before an input operation is useless, you need to check for failure afterwards instead.
2
votes

The fundamental problem is that int8_t is normally (apparently including your case) something like: typedef char int8_t;. For better or worse, iostreams provide overloads for char that assume the content is a character instead of a number.

It's possible to avoid this, such as by defining your own class such as:

class my_int8_t {
    // ...
};

In this case, you can provide your own overloads of operator>> and operator<< for that type (that treat the content as a number instead of a character).

Once you have that, copying the data from input to output (one number per line) is probably better done something like:

std::copy(std::istream_iterator<my_int8_t>(std::cin), 
          std::istream_iterator<my_int8_t>(),
          std::ostream_iterator<my_int8_t>(std::cout, "\n"));

Among things, this avoids a problem in your current code with detecting the end of file correctly.

1
votes
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

typedef unsigned char uint8_t;
class exstringstream : public stringstream
{
public:
    exstringstream(const string& s)
        :stringstream(s)
    {

    }
    friend exstringstream& operator >> (exstringstream&, uint8_t& t); 
};

exstringstream& operator >> (exstringstream& ss, uint8_t& t)
{
    unsigned int val;
    stringstream& s = ss;
    s >> val;

    t = static_cast<uint8_t>(val);

    return ss;
}

int main()
{
    string str("123 45");
    exstringstream ss(str);

    uint8_t a, b;
    ss >> a >> b;
    cout << a << " " << b << endl;
    return 0;
}
0
votes

The operators cannot tell the difference between character type and 8-bit signed/unsigned integer type since they are the same as seen from the language (as mentioned in other answers), meaning you also cannot implement a class/operator supporting both.

If you need both, you're forced to use scanf/printf with format "hhu", or PRIu8 from <cinttypes>:

https://www.cplusplus.com/reference/cinttypes/

https://www.cplusplus.com/reference/cstdio/printf/