When trying to compile this code:
std::fstream file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);
compiler will give me warning about oh-not-so-healthy conversion from unsigned char
to char
in call to write()
. As std::fstream
is in fact just a typedef for std::basic_fstream<char>
, one could think that using std::basic_fstream<uint8_t>
instead would allow them to compile above code without warning, as write()
expects pointer of template type.
This works, of course, but another problem pops out. Even though this code compiles perfectly fine:
std::basic_fstream<uint8_t> file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);
it will now fail on call to write()
, even though previous version was working (disregard compiler warnings). It took me a while to pinpoint where exception is thrown from in standard C++ library code, but I still don't really understand what's the case here. It looks like std::basic_fstream
uses a few character coding mechanism, and since there is one defined for char
but none for unsigned char
, the file stream fails silently when trying to use "wrong" character data type... That's how I see it, at least.
But that's also what I don't understand. There is no need for any character encoding. I don't even open file in text mode, I want to deal with binary data. That's why I use arrays of type uint8_t
, not char, it feels more natural to use this data type rather than plain old char
. But before I either decide to give up on uint8_t
data type and just accept working with char
buffers, or start using arrays of custom byte
datatype defined as char
, I'd like to ask two questions:
- What exactly is that mechanism that stops me from using unsigned character datatype? Is it really something related to character encoding, or does it serve some other purpose? Why file stream works fine with signed character data types, but not for unsigned ones?
- Assuming that I still would want to use
std::basic_fstream<uint8_t>
, regardless how (un)reasonable it is - is there any way to achieve that?
std::basic_fstream<unsigned char>
doesn't work because it usesstd::char_traits<unsigned char>
but the standard library doesn't provides such a specialisation, seestd::char_traits
for full details.If you'd like to read/write binary data, you need to use
std::basic_fstream<char>
, open it withstd::ios_base::binary
flag and usestd::basic_ostream<CharT,Traits>::write
function to write binary data.That's a bit of legacy since all
char
types can be used to represent binary data. The standard library useschar
probably because that's the shortest one to type and read that does the job.No
std::char_traits<unsigned char>
specialization.std::char_traits
has a few purposes exactly defined in its interface but that doesn't include decoding/encoding. The latter is done bycodecvt
, see the usage example there.Because
std::basic_ostream<CharT,Traits>::write
acceptsCharT
, the first template parameter you specify for the stream. It writes the same character type it reads and it uses thatcodecvt
to convert fromCharT
to bytes.The standard class and function templates cannot be specialized for built-in types, if I am not mistaken. You'd need to create another class with
std::char_traits
interface and specify that as the second template argument for the standard streams. I guess, you would need a pretty strong (philosophical) reason to roll up your sleeves and do that.If you don't, you may like to keep using
std::fstream<char>
and dostream.write(reinterpret_cast<char const*>(buf), sizeof buf);
.