CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 17

Hybrid View

  1. #1
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Mixing istream and FILE*

    I'm working with some of my own code which defines operator>>, and some C objects which support read() functions taking a FILE*. I would like to store both types of object in the same file, one after the other.

    At first I thought someone might have cleverly devised an istream-derived class which simply wraps a FILE*. Maybe it would be slower than an ifstream, but it would do what I need. However, after a bit of searching I've found no such thing, surprisingly.

    So my next thought was, I'll just open an ifstream, read the first object, tellg(), close it, open the FILE*, and fseek() to the same location.

    However, I'm not certain whether tellg() is guaranteed to return a result in bytes which would translate directly to the value needed by fseek(). Does anyone know what the story is there?

    Also, any better ideas for how I could manage this?

  2. #2
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: Mixing istream and FILE*

    You may find this article useful. It shows how to convert between fstream and FILE* handles, going through C file descriptors as an intermediate step. It might only work on Windows, though, as the article also mentions native Windows file handles.
    Last edited by HighCommander4; May 6th, 2010 at 05:56 PM.
    Old Unix programmers never die, they just mv to /dev/null

  3. #3
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Mixing istream and FILE*

    Yeah, fd() doesn't seem to be a standard fstream method as far as I can tell.

    The other option I found----fpos::get_fpos_t(), which would give me something to pass to fsetpos()----also appears to be Windows-only.

  4. #4
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Mixing istream and FILE*

    Since your C++ objects can write to streams, you can do that, and then write the stream internals to the FILE:

    Code:
    template <typename T>
    void writeToFileViaStream(FILE* file, const T& object, bool binaryMode = false)
    {
        std::ios_base::openmode openMode = std::stringstream::in |
                                           std::stringstream::out |
                                           (binaryMode ? std::binary : 0;
            
        std::stringstream ioss(openMode)
        ioss << object;
    
        //This part is copied verbatim from cplusplus.com
        //there might be better ways to do this, but I just wanted a quick proof of concept.
        //Watch out for memory leaks!
        {
            // get length of file:
            ioss.seekg (0, ios::end);
            length = is.tellg();
            ioss.seekg (0, ios::beg);
    
            // allocate memory:
            char buffer = new char [length];
    
            // read data as a block:
            ioss.read(buffer,length);
            ioss.close();
    
            fwrite(buffer, 1, length, file);
    
            delete[] buffer;
        }
    }
    This has the advantage of being pretty straight forward interface wise. It is just like fwrite, but can be used on C++ objects that only know operator>>.

    Heck, given the signature, I'm sure we could just call this function "fwrite" and it would work. I would not recommend doing that though :/

    The disadvantages include 2 extra writes: 1 to the stream, and another to a temporary buffer. I'm sure we can avoid the second copy, but I'm not concentrating on performance here.

    IMO, the BIG disadvantage of this method is that it wraps C++ functionality into a C interface. I would really have liked to do the opposite, but you just can't do much with a FILE, apart from writing to a file... You could try toying with the FILE buffer, but that is so dangerous I shudder at the very thought.

    (This of course also works for fread)

    Is this a satisfactory solution to your problem?
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  5. #5
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Mixing istream and FILE*

    I'm sure that will work great for writing....but then, for writing, I could simply write the C++ half, close the file, fopen() it again in append mode, and proceed. Writing isn't hard.

    The difficulty comes when I try to read it again.

    For now, I'm making the assumption that tellg() returns the file position in bytes, although I haven't been able to find any documentation which confirms that. I can pass that in fseek() easily enough; I know *that* operates in bytes. And the only downside of fseek() is that it takes a long argument, which is occasionally not big enough (operating on a 4GB+ file on a Win64 system....long remains a 32-bit type). Probably not going to be an issue in my case though.

  6. #6
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: Mixing istream and FILE*

    Well I was supposing you wanted something a bit more clever than opening/closing the file every time by hand. Although I guess you could do that inside the my "WriteViaStreamMethod", a side effect is that it would probably change your file handle, which the caller might not expect.

    I thought my method could also be used for reading, but I just now realized that there is no way to know in advance how many bytes will have to be read into the buffer, making my method not-usable.

    I just don't see any "elegant" way around this problem. I guess there is no way around this problem but opening closing the file every time.

    tellg() returns the file position in bytes, although I haven't been able to find any documentation which confirms that.
    tellg returns an iterator, so you don't even have the guarantee it returns an amount of bytes either! Just an offset from the begining. If you get the tellg from the beginning of the file, and the current tellg, then std:istance(tellg_first, tellg_current) should give you the amount of elements read so far by your stream. Since an element is pretty much guaranteed to be a char, I think it is safe* to assume that 1 element = 1 byte.

    *Just be careful of binary non-binary misalignment. If your file is in text mode, then a single element like "\n" could represent 2 bytes. However, ftell should have the same behavior too, so as long as you open both the stream and file in the same mode, you should be safe.

    That, and when opening your file, you can just do

    Code:
    long numbytes = myFile->ftell();
    
    ...
    Close File, open stream
    ...
    streampos current = myFstream.tellg(); //gets an iterator to "begin"
    std::advance(current, numbytes);
    myFstream.seekg(current);
    There, keep us posted on your findings.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  7. #7
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Re: Mixing istream and FILE*

    Another option would be to read the first object from the ifstream, then read the remainder of the file into a string, then use tmpfile() to generate a temporary file. fwrite() the string into the temporary file, seek back to the beginning, and proceed to read it.

    I'm not sure if the temporary file would be in memory or on disk, though. If it's in memory then the inefficiency essentially amounts to a couple of extra copies. If it's on disk, this is probably not ideal.

  8. #8
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    1,342

    Re: Mixing istream and FILE*

    Quote Originally Posted by Lindley View Post
    At first I thought someone might have cleverly devised an istream-derived class which simply wraps a FILE*. Maybe it would be slower than an ifstream, but it would do what I need. However, after a bit of searching I've found no such thing, surprisingly.
    I'm pretty sure gcc's implementation of fstream does this. Perhaps you could write your own based on that?
    Old Unix programmers never die, they just mv to /dev/null

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured