Grrr... C++ file I/O

O

Old Wolf

The header files <iostream>, <fstream> etc..
I mean what is iostream for?

It declares cin, cout, cerr, clog. Similar to how
stdio.h declares stdin, stdout, stderr.
why not just fstream or ifstream for ifstream and ofstream for
ofstream?

Huh? <fstream> defines both ifstream and ofstream.
<istream> includes other operations that apply to
all input streams (not just file ones).
 
S

SpreadTooThin

It declares cin, cout, cerr, clog. Similar to how
stdio.h declares stdin, stdout, stderr.


Huh? <fstream> defines both ifstream and ofstream.
<istream> includes other operations that apply to
all input streams (not just file ones).

so if I had a stream of data on a socket I could derive
a class from istream?
 
S

SpreadTooThin

Use ios::binary as a parameter the file open.
Use ifstream, tellg, seekg and read to read.
Use ofstream, tellp, seekp, and write to write.

Salt judiciously with std:: until your head swims.

if you are using fstream then do you use tellg or tellp?
 
M

Marcus Kwok

SpreadTooThin said:
if you are using fstream then do you use tellg or tellp?

tellg() tells you where the "get" pointer will be reading from.
tellp() tells you where the "put" pointer will write to.

On some implementations, the two pointers may be synchronized.
 
O

Old Wolf

so if I had a stream of data on a socket I could derive
a class from istream?

I suppose you could (although I haven't tried it).

Note that the streams interface is designed to be
used in a blocking manner, so if you were doing a
non-trivial socket application then this method
might not be to your taste.
 
C

Charles Bailey

I suppose you could (although I haven't tried it).

Note that the streams interface is designed to be
used in a blocking manner, so if you were doing a
non-trivial socket application then this method
might not be to your taste.

Note that basic_istream has precious few virtual functions so deriving
from the istream gives you poor opportunity for influencing the way
the stream interface behaves. If you look at how *stringstream and
*fstream classes work, they generally have very little added
functionality. They usually just have an explicit constructor to set
up a particular type of streambuf and perhaps a few other methods
which are particular to the type of streambuf involved.

If you want to change where you are reading and writing from in
istream/ostream calls (e.g. istream/ostream on top of a socket) then
you are probably better of deriving a custom class derived from
basic_streambuf. Then you only have to worry about where to put/get
the raw bytes from and you can construct istream/ostream interfaces
around your streambuf to provide all the formatting capabilities. Any
functionality which uses the [io]stream classes will be able to run
with the new streambuf without modification.

If you want the change the way in which various types are written out
then you may find that you can achieve what you want by implementing a
local::facet. You might, for instance, decide that integers should
really be serialised in a custom base64 style encoding. You could
then implement num_get and num_out facets. You can use this with the
standard iostreams by creating a new locale (std::locale has methods
for retrieving a copy of either the global locale or the "classic"
one), patching in your custom facets and imbueing (calling
ios_base::imbue) the stream with your custom locale.

The streams library is very flexible, but deriving from istream
directly probably won't give you the flexibility that you are looking
for.
 
J

James Kanze

As mentionned below, the main class you have to work with here
is streambuf; you derive from [io]stream only to automatically
create the correct streambuf.

And I can't remember any real application where I didn't do
this at least once.

Unless you're working in a multithreaded environment.

The real problem is the limited possibilties of error reporting.
Note that basic_istream has precious few virtual functions so deriving
from the istream gives you poor opportunity for influencing the way
the stream interface behaves. If you look at how *stringstream and
*fstream classes work, they generally have very little added
functionality. They usually just have an explicit constructor to set
up a particular type of streambuf and perhaps a few other methods
which are particular to the type of streambuf involved.
If you want to change where you are reading and writing from in
istream/ostream calls (e.g. istream/ostream on top of a socket) then
you are probably better of deriving a custom class derived from
basic_streambuf. Then you only have to worry about where to put/get
the raw bytes from and you can construct istream/ostream interfaces
around your streambuf to provide all the formatting capabilities. Any
functionality which uses the [io]stream classes will be able to run
with the new streambuf without modification.

In fact, this is the only way to change where you are reading
and writing from. The class derived from [io]stream is really
just for convenience, so that the user doesn't have to
explicitly declare his own streambuf object before declaring the
[io]stream object.
If you want the change the way in which various types are written out
then you may find that you can achieve what you want by implementing a
local::facet. You might, for instance, decide that integers should
really be serialised in a custom base64 style encoding. You could
then implement num_get and num_out facets. You can use this with the
standard iostreams by creating a new locale (std::locale has methods
for retrieving a copy of either the global locale or the "classic"
one), patching in your custom facets and imbueing (calling
ios_base::imbue) the stream with your custom locale.

That sounds a lot like abuse to me, and is likely to cause
trouble, since more complex objects like std::complex count on a
particular format. (In fact, std::complex doesn't even work
correctly in most European locales. That, I believe, is
considered an error, but I don't think the committee would
recognize it an error if e.g. it didn't work with
num_get/num_put facets which did binary output.)
The streams library is very flexible, but deriving from
istream directly probably won't give you the flexibility that
you are looking for.

No. It's more for convenience.

Deriving from ios is often done, however, for things like binary
streams, in order to get the standard error handling.
 
D

Diego Martins

As mentionned below, the main class you have to work with here
is streambuf; you derive from [io]stream only to automatically
create the correct streambuf.

And I can't remember any real application where I didn't do
this at least once.

Unless you're working in a multithreaded environment.

The real problem is the limited possibilties of error reporting.
Note that basic_istream has precious few virtual functions so deriving
from the istream gives you poor opportunity for influencing the way
the stream interface behaves. If you look at how *stringstream and
*fstream classes work, they generally have very little added
functionality. They usually just have an explicit constructor to set
up a particular type of streambuf and perhaps a few other methods
which are particular to the type of streambuf involved.
If you want to change where you are reading and writing from in
istream/ostream calls (e.g. istream/ostream on top of a socket) then
you are probably better of deriving a custom class derived from
basic_streambuf. Then you only have to worry about where to put/get
the raw bytes from and you can construct istream/ostream interfaces
around your streambuf to provide all the formatting capabilities. Any
functionality which uses the [io]stream classes will be able to run
with the new streambuf without modification.

In fact, this is the only way to change where you are reading
and writing from. The class derived from [io]stream is really
just for convenience, so that the user doesn't have to
explicitly declare his own streambuf object before declaring the
[io]stream object.
If you want the change the way in which various types are written out
then you may find that you can achieve what you want by implementing a
local::facet. You might, for instance, decide that integers should
really be serialised in a custom base64 style encoding. You could
then implement num_get and num_out facets. You can use this with the
standard iostreams by creating a new locale (std::locale has methods
for retrieving a copy of either the global locale or the "classic"
one), patching in your custom facets and imbueing (calling
ios_base::imbue) the stream with your custom locale.

That sounds a lot like abuse to me, and is likely to cause
trouble, since more complex objects like std::complex count on a
particular format. (In fact, std::complex doesn't even work
correctly in most European locales. That, I believe, is
considered an error, but I don't think the committee would
recognize it an error if e.g. it didn't work with
num_get/num_put facets which did binary output.)
The streams library is very flexible, but deriving from
istream directly probably won't give you the flexibility that
you are looking for.

No. It's more for convenience.

Deriving from ios is often done, however, for things like binary
streams, in order to get the standard error handling.

--
James Kanze (GABI Software, from CAI) email:[email protected]
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

there are two C++ important things I never found in a C++ mainstream
book:
- a detailed description/tutorial/pitfall on implementing custom
streams;
- a detailed description/tutorial/pitfall on implementing custom
iterators;

why? :(
 
C

Charles Bailey

That sounds a lot like abuse to me, and is likely to cause
trouble, since more complex objects like std::complex count on a
particular format. (In fact, std::complex doesn't even work
correctly in most European locales. That, I believe, is
considered an error, but I don't think the committee would
recognize it an error if e.g. it didn't work with
num_get/num_put facets which did binary output.)

My example was bad. The usual facets are fairly flexible but in
theory, if you have an obscure locale with an interesting way of
writing numbers that you need to support (or have invented ;) ) you
should be able to provide your own locale and not break everything if
your locale is consistent.

I haven't had to work with serializing complex numbers but I hadn't
realised how broken it apprears to be.

#include <iostream>
#include <sstream>
#include <complex>
#include <locale>

int main(int argc, char* argv[])
{
std::stringstream sstr;

if (argc > 1)
{
sstr.imbue(std::locale(argv[1]));
}

std::complex<double> c1(1.5), c2(1, 5);
std::complex<double> c3, c4;

sstr << c1 << c2;
std::cout << sstr.str() << std::endl;
sstr >> c3 >> c4;

std::cout << c3 << c4 << std::endl;
}

$ ./a.out
(1.5,0)(1,5)
(1.5,0)(1,5)

OK, that works.

$ ./a.out fr_FR
(1,5,0)(1,5)
(1.5,0)(1.5,0)

Oops!

$ ./a.out en_GB
(1.5,0)(1,5)
(1.5,0)(0,0)

This one surprised me. Attaching a debugger reveals that the comma in
(1,5) is parsed as part of the real part as ',' is a valid thousands
separator. The real read stops when it hits the ')' and then flags a
failure as the thousands seperator was in the wrong place. At least
that's my interpretation of what my implementation is doing.
 
J

James Kanze

there are two C++ important things I never found in a C++ mainstream
book:
- a detailed description/tutorial/pitfall on implementing custom
streams;
- a detailed description/tutorial/pitfall on implementing custom
iterators;

I don't know? I wrote two articles about one type of custom
stream, some years ago in C++ Reports; they're available at my
site (http://kanze.james.neuf.fr/articles-en.html). For a long
time, Dietmar Kuehl had some examples at his site, but I don't
know what the status of that is now. And while I've not seen it
myself (I was doing this before the book came out), I've heard
only good about _Standard C++ IOStreams and Locales: Advanced
Programmer's Guide and Reference_, by Angelika Langer and Klaus
Kreft.

In the meantime, you should probably check out Boost::iostream.
While it's not really that difficult to write your own
streambuf, the Boost library makes it exceptionally easy, even
when you want to handle more complicated issues like seeking or
buffering.
 
J

James Kanze

My example was bad. The usual facets are fairly flexible but in
theory, if you have an obscure locale with an interesting way of
writing numbers that you need to support (or have invented ;) ) you
should be able to provide your own locale and not break everything if
your locale is consistent.

You should be able to, at least within very liberal limits. In
practice, I'm less sure. I would consider something like using
a different base an abuse, and in practice, I'd be sceptical of a
facet which provided a new num_get or num_put (as opposed to
simply a new num_punct). As you say, you should be able to,
but...
I haven't had to work with serializing complex numbers but I hadn't
realised how broken it apprears to be.

It is very, very broken, probably because it doesn't take locale
at all into account. This will be fixed, I hope, in the next
release of the standard; at present, it fails in most of the
locales I normally use.
#include <iostream>
#include <sstream>
#include <complex>
#include <locale>
int main(int argc, char* argv[])
{
std::stringstream sstr;
if (argc > 1)
{
sstr.imbue(std::locale(argv[1]));
}
std::complex<double> c1(1.5), c2(1, 5);
std::complex<double> c3, c4;
sstr << c1 << c2;
std::cout << sstr.str() << std::endl;
sstr >> c3 >> c4;
std::cout << c3 << c4 << std::endl;

}
$ ./a.out
(1.5,0)(1,5)
(1.5,0)(1,5)
OK, that works.
$ ./a.out fr_FR
(1,5,0)(1,5)
(1.5,0)(1.5,0)

The normal way of writing a complex in France is "(1,5;1,5)".
For obvious reasons, we don't use a , as a separator.
$ ./a.out en_GB
(1.5,0)(1,5)
(1.5,0)(0,0)
This one surprised me. Attaching a debugger reveals that the
comma in (1,5) is parsed as part of the real part as ',' is a
valid thousands separator.

The whole way thousands separators work probably needs to be
rethought out. The usual thousands separator in France is a
space, which causes no end of problems. (I'm afraid I don't
have a solution to propose for this one. My own input
conversion routines ignore spaces, where ever they appear. But
this means that the user must first parse the text enough to
isolate the number before trying to convert it.)
The real read stops when it hits the ')' and then flags a
failure as the thousands seperator was in the wrong place. At
least that's my interpretation of what my implementation is
doing.

It sounds likely. There should be a means of inhibiting the use
of thousands separators in certain numbers, regardless of the
locale, and this should be activated when reading complex.
 

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
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top