The fwrite()
function in C uses const void *restrict buffer
as the first argument, so you can pass pointer to your struct
as the first parameter directly.
http://en.cppreference.com/w/c/io/fwrite
e.g. fwrite(&someStruct, sizeof(someStruct), 1, file);
But in C++, the ostream::write()
requires const char_type*
, which forces you to use reinterpret_cast
. (In Visual Studio 2013, it's const char*
.)
http://en.cppreference.com/w/cpp/io/basic_ostream/write
e.g. file.write(reinterpret_cast<char*>(&someStruct), sizeof(someStruct));
In almost all cases, the binary data to be written to files is not a char
array, so why does the standard prefer the style which seems more complex?
P.S.
1. Actually I used the write()
method in ofstream
with ios::binary
mode, but according to the reference, it inherits ofstream
. So I use ostream::write()
above.
2. If you want to print a stream of characters, you could use operator<<()
.
Isn't write()
method designed for writing raw data?
3. If write()
is not the way to write binary data, then what is the way to do it within the standard? (Although this may bother portability of the code due to various memory align strategies on different platforms)
The portrayal of this as a C vs C++ thing is misleading. C++ provides std::fwrite(const void*, ...)
just like C. Where C++ chooses to be more defensive is specifically the std::iostream
versions.
"Almost in all cases the binary data to be written to files is not char array"
That's debatable. In C++ isn't not unusual to add a level of indirection in I/O, so objects are streamed or serialised to a convenient - and possibly portable (e.g. endian-standardised, without or with standardised structure padding) - representation, then deserialised/parsed when re-read. The logic is typically localised with the individual objects involved, such that a top-level object doesn't need to know details of the memory layout of its members. Serialisation and streaming tends to be thought of / buffered etc. at the byte level - fitting in better with character buffers, and read()
and write()
return a number of characters that could currently be transmitted - again at the character and not object level - so it's not very productive to pretend otherwise or you'll have a mess resuming partially successful I/O operations.
Raw binary writes / reads done naively are a bit dangerous as they don't handle these issues so it's probably a good thing that the use of these functions is made slightly harder, with reinterpret_cast<>
being a bit of a code smell / warning.
That said, one unfortunate aspect of the C++ use of char*
is that it may encourage some programmers to first read to a character array, then use inappropriate casts to "reinterpret" the data on the fly - like an int*
aimed at the character buffer in a way that may not be appropriately aligned.
If you want to print a stream of characters, you could use
operator<<()
. Isn'twrite()
method designed for writing raw data?
To print a stream of characters with operator<<()
is problematic, as the only relevant overload takes a const char*
and expects a '\0'
/NUL-terminated buffer. That makes it useless if you want to print one or more NULs in the output. Further, when starting with a longer character buffer operator<<
would often be clumsy, verbose and error prone, needing a NUL
swapped in and back around the streaming, and would sometimes be a significant performance and/or memory use issue e.g. when writing some - but not the end - of a long string literal into which you can't swap a NUL, or when the character buffer may be being read from other threads that shouldn't see the NUL.
The provided std::ostream::write(p, n)
function avoids these problems, letting you specify exactly how much you want printed.
char_type
is not exactly char *
, it's the template parameter of the stream that represents the stream's character type:
template<typename _CharT, typename _Traits>
class basic_ostream : virtual public basic_ios<_CharT, _Traits>
{
public:
// Types (inherited from basic_ios):
typedef _CharT char_type;
<...>
And std::ostream
is just the char instantiation:
typedef basic_ostream<char> ostream;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With