Why doesn't uint8_t and int8_t work with file and console streams? [duplicate]

拜拜、爱过 提交于 2019-12-14 03:22:18

问题


$ file testfile.txt
testfile.txt: ASCII text

$ cat testfile.txt 
aaaabbbbccddef

#include <iostream>
#include <fstream>
#include <string>
#include <cstdint>
typedef uint8_t byte; // <-------- interesting
typedef std::basic_ifstream<byte> FileStreamT;
static const std::string FILENAME = "testfile.txt";
int main(){
    FileStreamT file(FILENAME, std::ifstream::in | std::ios::binary);
    if(!file.is_open())
        std::cout << "COULD NOT OPEN FILE" << std::endl;
    else{
        FileStreamT::char_type buff;
        file.read(&buff,1);
        std::cout << (SOMECAST)buff; // <------- interesting
    }
    std::cout << "done" << std::endl;
}

Depending on what is in the typedef and what is it casted to (or not casted), it does all sorts of stupid things.

It happens to work with 'typedef char' and no cast. (97 when casted to int, as expected)

Both uint8_t and int8_t will print

  • nothing without cast

  • nothing when casted to char or unsigned char

  • 8 when casted to int or unsigned (although ASCII 'a' should be 97)

I somehow managed to print a "�" character, but forgot which case it was.

Why do I get these strange results?

notes for the future reader:

takeaway from the answer given: only instantiate streams with char (or one of the wide characters also mentioned by the standard), otherwise you get no compiler warning and silent failure

it is very sad that the standard warrants these things

moral of the story: avoid C++


回答1:


The declaration of template std::basic_ifstream is:

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>
> class basic_ifstream;

The C++03 Standard (21.1/1) requires the library to define specializations of std::char_traits<CharT> for CharT = char, wchar_t.

The C++11 Standard (C++11 21.2/1) requires the library to define specializations of std::char_traits<CharT> for CharT = char,char16_t,char32_t,wchar_t.

If you instantiate std::basic_ifstream<Other> with Other not one of the 2[4] types nominated by the Standard to which you are compiling then the behaviour will be undefined, unless you yourself define my_char_traits<Other> as you require and then instantiate std::basic_ifstream<Other,my_char_traits<Other>>.

CONTINUED in response to OP's comments.

Requesting an std::char_traits<Other> will not provoke template instantiation errors: the template is defined so that you may specialize it, but the default (unspecialized) instantiation is very likely to be wrong for Other or indeed for any given CharT, where wrong means does not satisfy the the Standard's requirements for a character traits class per C++03 § 21.1.1/C++11 § 21.2.1.

You suspect that a typedef might thwart the choice of a template specialization for the typedef-ed type, i.e. that the fact that uint8_t and int8_t are typedefs for fundamentals character types might result in std::basic_ifstream<byte> not being the same as std::basic_ifstream<FCT>, where FCT is the aliased fundamental character type.

Forget that suspicion.typedef is transparent. It seems you believe one of the typedefs int8_t and uint8_t must be char, in which case - unless the typedef was somehow intefering with template resolution - one of the misbehaving basic_ifstream instantiations you have tested would have to be std::basic_ifstream<char>

But what about the fact that typedef char byte is harmless? That belief that either int8_t or uint8_t = char is false. You will find that int8_t is an alias for signed char while uint8_t is an alias for unsigned char. But neither signed char nor unsigned char is the same type as char:

C++03/11 § 3.9.1/1

Plain char, signed char, and unsigned char are three distinct types

So both char_traits<int8_t> and char_traits<uint8_t> are default, unspecialized, instantiations of template char_traits and you have no right to expect that they fulfill that Standard's requirements of character traits.

The one test case in which you found no misbehaviour was for byte = char. That is because char_traits<char> is a Standard specialization provided by the library.

The connection between all the misbehaviour you have observed and the types that you have substituted for SOMECAST in:

std::cout << (SOMECAST)buff; // <------- interesting

is none. Since your testfile contains ASCII text, basic_ifstream<char> is the one and only instantiation of basic_ifstream that the Standard warrants for reading it. If you read the file using typedef char byte in your program then none of the casts that you say you substituted will have an unexpected result: SOMECAST = char or unsigned char will output a, and SOMECAST = int or unsigned int will output 97.

All the misbehaviour arises from instantiating basic_ifstream<CharT> with CharT some type that the Standard does not warrant.



来源:https://stackoverflow.com/questions/17874122/why-doesnt-uint8-t-and-int8-t-work-with-file-and-console-streams

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!