ostream::write

R

Ralf Goertz

Hi,

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?
 
V

Vaclav Haisman

Ralf Goertz wrote, On 30.4.2009 11:12:
Hi,

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?
I suspect two reasons: 1st, char is what streams are about, and 2nd, aliasing
issues. Maybe even 3rd, type safety. The former forces you to stop and think
and add the cast to char* while the latter accepts any pointer.
 
R

Ralf Goertz

James said:
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?
 
B

Bo Persson

Ralf said:
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));

Not really.

Using using a basic_ostream<char> with Foo elements shouldn't be easy.

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.


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
 
R

Ralf Goertz

Bo said:
Not really.

Using using a basic_ostream<char> with Foo elements shouldn't be easy.



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.

Thanks for the explanation.
Forcing you to use a visible cast to do it, makes perfect sence.

I see that now.
 
J

James Kanze

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.
 
J

James Kanze

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.
 
B

Bo Persson

James said:
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
 
R

Ralf Goertz

James said:
Not if you expect to reread it later.


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.
 
J

James Kanze

James Kanze wrote: [...]
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.
 
R

Ralf Goertz

James said:
On May 3, 6:22 pm, Ralf Goertz

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.
 
J

James Kanze

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,160
Messages
2,570,889
Members
47,420
Latest member
ZitaVos505

Latest Threads

Top