passing around iostreams

passing around iostreams

Post by homsan tof » Sun, 27 Jul 2003 20:02:05



Hi,

Passing iostream objects to functions and constructors seems like it might
warrant an entry in the FAQ (since it looks like it's asked in several %'s
of the 25,000+ entries in a search for "ifstream" on google.groups).

Anyway, after browsing there for a while, I'd still like some advise:
My factory class/function should open an ifstream, read enough to
detect file type (eg which soundfile format), then create a reader
object for the file.
Of course I can call close(), then pass the filename to
the reader constructor and reopen the file there.
Is this then the recommended way?

Else, I'd like it to look something like:

IReader * factory(string filename) {
     ifstream ifile(filename.c_str());
     int magic;
     ifile >> magic;
     if (magic == 0x46726f6f) // "Froo"
         return new FroobleReader( ifile );

     // ...etc...

Quote:}

where FroobleReader is already declared something like:

class FroobleReader : public IReader
{
     istream _input;
public:
     FroobleReader( istream & instr ) {
         // now, how to init _input from instr?
     }

Quote:};

The first impulse (to me, and most newbies, going on the mentioned google results)
was to pass the ifstream by ref to the constructor.
This doesn't work since the factory stream (ifile) goes out of scope.
But can the ifstream somehow be cloned "in effect", if not as object?
- eg by initing _input from the instr.rdbuf() object somehow?

Thanks,

        S?ren

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

passing around iostreams

Post by Ulrich Eckhard » Mon, 28 Jul 2003 19:49:58


 > Anyway, after browsing there for a while, I'd still like some advise:
 > My factory class/function should open an ifstream, read enough to
 > detect file type (eg which soundfile format), then create a reader
 > object for the file.
 > Of course I can call close(), then pass the filename to
 > the reader constructor and reopen the file there.
 > Is this then the recommended way?
 >
 > Else, I'd like it to look something like:
 >
 > IReader * factory(string filename) {
 >      ifstream ifile(filename.c_str());
 >      int magic;
 >      ifile >> magic;
 >      if (magic == 0x46726f6f) // "Froo"
 >          return new FroobleReader( ifile );
 >
[snip]
 >
 > The first impulse (to me, and most newbies, going on the mentioned google
 > results) was to pass the ifstream by ref to the constructor.
 > This doesn't work since the factory stream (ifile) goes out of scope.
 > But can the ifstream somehow be cloned "in effect", if not as object?
 > - eg by initing _input from the instr.rdbuf() object somehow?

Nope. The result of rdbuf() points to an object that just as well goes out
of scope. However, what you can do is this:

auto_ptr<ifstream> in(new ifstream(filename));
int magic;
(*in) >> magic;

If you find a suitable parser for the magic, you can then hand the
auto_ptr<> to its ctor, which also makes clear who has ownership of the
object.

Just a note: iostreams are the proper class for text-based IO only. If you
intend to go with binary fileformats, you should rather take
streambuffers. The principle stays the same. Also, when the binary/textual
question is resolved on a case-by-case study, you can still attach the
streambuffer to a normal stream for textual IO.

On a second note, remember that there is something that you cant do once
you opened the file: switch the ios_base::binary flag. Another thing is
the codeconversion facet (the part of the locale that does the conversion
between external bytes and the internal charset) which can't be changed
once any IO-operations have taken place.

Uli

--
Questions ?
see  C++-FAQ Lite: http://parashift.com/c++-faq-lite/  first !

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

passing around iostreams

Post by Siemel Nara » Tue, 29 Jul 2003 02:09:56



Quote:> IReader * factory(string filename) {
>      ifstream ifile(filename.c_str());
>      int magic;
>      ifile >> magic;
>      if (magic == 0x46726f6f) // "Froo"
>          return new FroobleReader( ifile );

>      // ...etc...
> }
> class FroobleReader : public IReader
> {
>      istream _input;
> public:
>      FroobleReader( istream & instr ) {
>          // now, how to init _input from instr?
>      }
> };

Maybe instead of istream _input you could use std::auto_ptr<std::istream>
_input?

--
+++++++++++
Siemel Naran

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

passing around iostreams

Post by homsan tof » Tue, 29 Jul 2003 18:49:02


Thanks for your clear & detailed comments!



>  > But can the ifstream somehow be cloned "in effect", if not as object?
>  > - eg by initing _input from the instr.rdbuf() object somehow?

> Nope. The result of rdbuf() points to an object that just as well goes out of scope.

...though I guess I *could* switch the buf using the interface
     basic_streambuf * basic_ios::rdbuf( basic_streambuf * );
so I get something like

MyReader * readerFactory(string filename)  // global function or method of some class
{
   ifstream in(filename, std::ios::binary);
   // ....some MyReader descendant is selected
   if (magic == whatever)
       return new FroobReader( in.rdbuf(new stringbuf) );

   // ....

Quote:}

and the MyReader base class is defined as

class MyReader {
   istream _input;
public:
   MyReader(streambuf *sb) : _input(sb) { }
//...

Quote:};

- but would it be a good idea?

Quote:> However, what you can do is this:
> auto_ptr<ifstream> in(new ifstream(filename));

> If you find a suitable parser for the magic, you can then hand the
> auto_ptr<> to its ctor, which also makes clear who has ownership of the
> object.

That's better I suppose. And the new owner has an auto_ptr<ifstream>
member(?).
Btw I'm now clearer that I want MyReader to have an istream, not an ifstream,
for flexibility. So I'd prefer an auto_ptr<istream>, but that's no problem.

Quote:> Just a note: iostreams are the proper class for text-based IO only. If you
> intend to go with binary fileformats, you should rather take streambuffers.
>  The principle stays the same.

All data will be binary (eg audio).
Sorry, do you mean using a streambuf (pointer?) directly as member of
my Reader class without the istream carrier/wrapper?
The streambuf::sgetn() method seems a bit low-level for reading data...?

Thanks for your patience,

        Homsan

(meanwhile I'll try out your suggestions of course)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

passing around iostreams

Post by Ulrich Eckhard » Wed, 30 Jul 2003 19:09:39


 > MyReader * readerFactory(string filename)
 > {
 >    ifstream in(filename, std::ios::binary);
 >    // ....some MyReader descendant is selected
 >    if (magic == whatever)
 >        return new FroobReader( in.rdbuf(new stringbuf) );

As soon as you return here, the ifstream goes out of scope and its
streambuffer goes with it. In the case of fstreams, the streambuffer is
part of the stream and in any case it is not deleted by the iostream
baseclass.
  Try taking a look at the code for fstreams. They are just a thin layer
that adds a filebuf and a few methods to access the filebuf's
open/close/is_open (well, at least once you see through the complex
inheritance-relations of those classes).

 >> Just a note: iostreams are the proper class for text-based IO only. If
 >> you intend to go with binary fileformats, you should rather take
 >> streambuffers. The principle stays the same.
 >
 > All data will be binary (eg audio).
 > Sorry, do you mean using a streambuf (pointer?) directly as member of
 > my Reader class without the istream carrier/wrapper?
 > The streambuf::sgetn() method seems a bit low-level for reading data...?

Yes, that is what I mean. The reasons are:
- implicit documentation. A stream is for textual IO while a streambuffer
is for the actual buffering and transport. In case someone else needs to
maintain your program, they will never be tempted to use any of the
formatted IO-functions of the stream if you use a streambuffer.
- overhead. A stream consists of a few formatting-flags, a width-field, a
locale, a map with iwords/pwords and finally a streambuffer (plus some
things I forgot). You don't need most of these things, you just want to
push bytes without any of that formatting.

cheers

Uli

--
Questions ?
see  C++-FAQ Lite: http://parashift.com/c++-faq-lite/  first !

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

passing around iostreams

Post by homsan tof » Thu, 31 Jul 2003 08:14:14


Thanks for your tips Uli, this clarifies some more things.
- I did find out about the "sticky" character of streambuf ownership
yesterday (from some deeper google-groping), but haven't seen the
bare streambuf use yet. Great, I'll go for that then!

Thanks,

        homsan

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

 
 
 

1. Passing polymorphic objects around.

I have an interesting problem. I want to allow polymorphic objects to be
passed around my program in a safe and easy way. In particular, I want to
allow objects of my class to own polymorphic objects that are passed by
reference to the constructor. Here is my class so far.

class MyClass
{
    Base &object; // polymorphic object
public:
    MyClass(Base &object_) : object(object_) { ... }

The problem I see there is that the member variable makes reference to an
object that may not exist. The solution, then, must be to make a copy of the
polymorphic object. The new version of my class might be:

class MyClass
{
    Base *object;
public:
    MyClass(Base &object)
    {
        this->object = &object.clone();
    }
    ~MyClass()
    {
        delete object;
    }

The clone method of the Base class is a pure virtual function. Here is the
Base class with the pure virtual function declaration.

class Base
{
    ...
public:
    ...
    virtual Base &clone() = 0;

Derived classes must define a clone method. This is easy when the derived
class has a suitable copy constructor. Here is the derived class.

class Derived : public Base
{
public:
    Derived(const Derived &rhs) { ... }
    ...
    Base &clone()
    {
        return *(new Derived(*this));
    }

With such a mechanism in place the following code fragments will not produce
an object of my class that is corrupt. Without it they do. Derived is any
class that derives from Base with a suitable clone method.

// CODE FRAGMENT 1
MyClass myobject(Derived(...));

// CODE FRAGMENT 2
MyClass func(Derived derived)
{
    return MyClass(derived);

// CODE FRAGMENT 3
Base &object = *(new Derived(...));
MyClass myobject(object);
delete &derived;

In the first code fragment the object of Derived goes out of scope once
myobject has been created. In the second code fragment the object of Derived
goes out of scope at the end of the function. In the third code fragment the
polymorphic object is deleted after myobject has been created. Even so, the
fragments produce healthy objects of my class.

I would appreciate any feedback on this issue. Perhaps there are things I
have missed or mistakes I have made that should be pointed out.

2. HELP! Running EXE files without EXEC!!!

3. Passing data around in an object system

4. How to prevent app from showing on task bar

5. passing a class instance around

6. Free subscription-Music Classifieds

7. passing this around

8. Network client administrator

9. how do I get around not being able to pass parameters to new AClass[n]

10. how do I get around not being able to pass parameters to new

11. Problem with bind2nd and passing an iostream as the second...

12. passing iostreams

13. What's the difference between #include <iostream> and #include <iostream.h>?