3

Consider this simple C++ program:

#include <cstdint>
#include <iostream>

int main() {
    uint8_t var;
    std::cin >> var;
    std::cout << "var = " << var << '\n';
    if (var == 1) {
        std::cout << "var is 1\n";
    } else {
        std::cout << "var is not 1\n";
    }
}

Running it shows some surprising behavior. When the input is 1, the output is

var = 1
var is not 1

which is clearly absurd! After quite a few varied tests, I realized what is happening—it's reading and writing a char! Still, it's not the behavior I want—I used uint8_t because I want the integer behavior. How can I make uint8_t behave like the other integer types when doing stream I/O? Alternatively, what one-byte type should I use instead?

Maya
  • 1,490
  • 12
  • 24
  • 1
    If you have C++17, switch to using `std::byte`. Then you can overload the input and output operators to "do the right thing" – NathanOliver Oct 18 '19 at 17:45
  • 1
    `uint8_t` gets inputted as a char, so its value is 31, not 1. – lakeweb Oct 18 '19 at 17:48
  • @lakeweb read the question. I already know that. I'm not asking what's happening, but how to get more reasonable behavior. – Maya Oct 18 '19 at 17:49
  • You can't. You have to read an `unsigned int` and then convert it to `uint8_t`. – Brian Bi Oct 18 '19 at 17:51
  • @NathanOliver Thanks! I mostly use C++ for small, one-off programs, so this would be somewhat overkill. I'll probably still switch to `std::byte` to avoid getting bitten by this in the future. Though, is there anything built-in that's really an integer? – Maya Oct 18 '19 at 17:52
  • @NieDzejkob Unfortunately not. The smallest "int type" is `short int`. Anything smaller is typically `bool` or `char`, with `bool` not really being suitable. – NathanOliver Oct 18 '19 at 17:54
  • The question is the answer. The std::stream will treat a byte as a char. As said in the answers, tell the std::stream to treat it otherwise. And it _one-off_ stuff, I would just use the C cast. – lakeweb Oct 18 '19 at 17:57
  • There's [this answer](https://stackoverflow.com/a/21389821/7359094) to a similar question, but about `std::cout`. – François Andrieux Oct 18 '19 at 18:03
  • @FrançoisAndrieux Oh durp. Somehow I missed the namespace. – NathanOliver Oct 18 '19 at 18:10

2 Answers2

3

You need to cast to int:

std::cout << "var = " << static_cast<int>(var) << '\n';

or shorter (C-style):

std::cout << "var = " << (int)var << '\n';   //or:
std::cout << "var = " << int(var) << '\n';   //constructor-like

or even shorter (promoting to int with an arithmetic operator):

std::cout << "var = " << +var << '\n';
bloody
  • 1,131
  • 11
  • 17
  • 2
    While the last one is shorter, I would argue that it's clever/surprising so I would personally advise against it. – Borgleader Oct 18 '19 at 18:11
3

How can I make uint8_t behave like the other integer types when doing stream I/O?

You can't. Apparently you can.

If std::uint8_t is an alias of unsigned char, as it usually (maybe always) is, then it is a character type, and the standard streams treat it as a character type.

You can convert it to a non-character integer type before inserting to a stream:

 std::cout << "var = " << static_cast<unsigned>(var) << '\n';

Or with an intermediary variable:

unsigned temp = var;
std::cout << "var = " << temp << '\n';

Stream extraction works only with the intermediary variable approach:

unsigned temp;
std::cin >> temp;
var = temp;

On a related note, if you wish to output the address of a variable, then std::cout << &var; won't work, because it will be treated as a null terminated string... which it isn't and thus results in undefined behaviour. To achieve that, you can use std::cout << static_cast<void*>(&var);.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Fair enough. Is there an equivalent for `cin`? – Maya Oct 18 '19 at 17:54
  • `unsigned temp = var;` this is an implicit conversion to `int` thus idiomatically uglier. Giving that syntactically also bulkier it is pointless if compared to a direct cast. – bloody Oct 19 '19 at 08:42