what is the reason that
ostream& ostream::write ( const char* s , streamsize n );
is not defined as
ostream& ostream::write ( const void* p , streamsize n );
like its C-counterpart?
--
VH
> is not defined as
> like its C-counterpart?
That's because it doesn't make sense calling it with anything
but a char buffer. (The same is true for fwrite, but C has
never insisted that things make sense.)
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> On Apr 30, 11:12 am, Ralf Goertz
> <r_goe...@expires-2006-11-30.arcornews.de> wrote:
>
>> what is the reason that
>
>> ostream& ostream::write ( const char* s , streamsize n );
>
>> is not defined as
>
>> ostream& ostream::write ( const void* p , streamsize n );
>
>> like its C-counterpart?
>
> That's because it doesn't make sense calling it with anything
> but a char buffer.
Why is that? Suppose I have a
struct Foo {int i; double d;} bar;
Isn't it perfectly sensible to write
os.write(reinterpret_cast<char *>(&bar),size_of(Foo));
using some variable os of type ostream? Of course the resulting file
wouldn't be portable. But portability put aside, I don't see why I'm
forced to cast the pointer.
> (The same is true for fwrite, but C has never insisted that things
> make sense.)
In this case, couldn't it be the other way round?
Not really.
Using using a basic_ostream<char> with Foo elements shouldn't be easy.
Why are you not using a basic_ostream<Foo> for Foo objects? :-)
>
> using some variable os of type ostream? Of course the resulting file
> wouldn't be portable. But portability put aside, I don't see why I'm
> forced to cast the pointer.
>
>> (The same is true for fwrite, but C has never insisted that things
>> make sense.)
>
> In this case, couldn't it be the other way round?
No, C lacks overloads for functions taking parameters of varying
types. Often the library has to punt, and take a void* parameter
instead. Using C++ we don't have to do that. There is no reason to
make it easy to abuse the type system.
Forcing you to use a visible cast to do it, makes perfect sence.
Bo Persson
Thanks for the explanation.
> Forcing you to use a visible cast to do it, makes perfect sence.
I see that now.
> >> what is the reason that
> >> ostream& ostream::write ( const char* s , streamsize n );
> >> is not defined as
> >> ostream& ostream::write ( const void* p , streamsize n );
> >> like its C-counterpart?
> > That's because it doesn't make sense calling it with anything
> > but a char buffer.
> Why is that? Suppose I have a
> struct Foo {int i; double d;} bar;
> Isn't it perfectly sensible to write
> os.write(reinterpret_cast<char *>(&bar),size_of(Foo));
> using some variable os of type ostream?
Not if you expect to reread it later.
> Of course the resulting file wouldn't be portable. But
> portability put aside, I don't see why I'm forced to cast the
> pointer.
Because the resulting file isn't even portable to future
releases of the compiler, or compiling with different compiler
options. If you're using the file as temporary memory, to be
reread later in the same execution of the program, fine.
Otherwise no.
> Why are you not using a basic_ostream<Foo> for Foo objects? :-)
Because in practice, it's far too much work; in fact, in
practice, I'm not sure it's possible, at least not portably.
You've got to define the corresponding char_traits for <Foo>,
with an int_type as well as a char_type, and streampos and
streamoff. In practice, the templatization of iostream is a
disaster---about the only good thing you can say about it is
that it can be ignored.
Yes, that's exactly what my smiley said!
Having a templated basic_ostream<T> with a write(T*, ) member
function, it is obviously intended to write Ts and not much else.
Otherwise the write member function would have been templated too,
like template<class U> write(U, ).
For ostream we actually have something like that, it is just a
freestanding operator<<(ostream&, const Foo&) that the OP would have
to add. The potential performance difference of formatting the Foo
content is of course very unlikely to be noticed, if the application
does anything else that writing Foos.
Bo Persson
Now I am even more confused. How am I supposed to write an object of
type Foo to a file? I know about serialization, but sometimes one needs
to write something like an array of 10 unsigned 32 bit integers.
> > Because the resulting file isn't even portable to future
> > releases of the compiler, or compiling with different
> > compiler options. If you're using the file as temporary
> > memory, to be reread later in the same execution of the
> > program, fine. Otherwise no.
> Now I am even more confused. How am I supposed to write an
> object of type Foo to a file? I know about serialization, but
> sometimes one needs to write something like an array of 10
> unsigned 32 bit integers.
You "serialize" it. Regardless of the object type, you define a
format (or adopt an existing format, like XDR), then implement
code to read and write that format.
> On May 3, 6:22 pm, Ralf Goertz
>> Now I am even more confused. How am I supposed to write an object of
>> type Foo to a file? I know about serialization, but sometimes one
>> needs to write something like an array of 10 unsigned 32 bit
>> integers.
>
> You "serialize" it. Regardless of the object type, you define a
> format (or adopt an existing format, like XDR), then implement code to
> read and write that format.
But that brings me to my original question. If I serialize the members
using „htonl“ and friends I still end up with binary data addresses
/not/ in char* format for which I would need a reinterpret_cast for
writing. XDR seems to offer a char*, but still I don't get why I need a
cast for a perfectly portable network representation of a long int.
Who said anything about using htonl? htonl is a hack, which
works on a few systems, but isn't generally applicable. Even
where it's applicable, it isn't without problems, e.g. alignment
questions. And there are protocols which use different
representations than what htonl would give, where it is
available. Define your format, then implement it. It's really
not that difficult for integer types. And that way, you know
what you've got.