5

I need to check whether a std::fstream opened a file in read and/or write mode.

So far, I found iosbase::openmode, but I don't think I can access it.

Is there any other way?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Tom Tom
  • 1,127
  • 11
  • 25
  • 5
    It's a great question. I can't think of a way. I think you'll need to wrap the stream and record the openmode in the wrapper. – Richard Hodges May 08 '16 at 11:36
  • 1
    This is for ofstream but it should work for fstream as well: http://stackoverflow.com/questions/13497799/change-or-check-the-openmode-of-a-stdofstream – Striker May 08 '16 at 11:43
  • Reading and writing are fundamentally different operations. If you want to read from a file, you open the file in read mode; if you want to write to a file, you open the file in write mode. Note the order here: you decide **what** you want to do, and **then** you decide how to open the file. Deciding what to do based on how the file was opened is inside out. (before it was edited, the question mentioned doing what MFC does with `CArchive`; don't take anything in MFC as a guide for sound design) – Pete Becker May 08 '16 at 13:29
  • @Pete, perhaps MFC Serialize is not a good Design, but is an elegant way to do that, If we can not distingush if it is opened on write or read mode, we have to write two separate functions, ex LoadParameter(), SaveParameter() in each corresponding class(es) – Tom Tom May 08 '16 at 19:14
  • @TomTom - yes, you have to write two functions. That's the point: you're doing two different things, and that means two different functions. Mushing them into a single function just makes a function that's hard to understand because it does too much. – Pete Becker May 08 '16 at 20:15
  • @PeteBecker: MFC's serialization uses a single function for good reasons. Since serialization and de-serialization must match, it's natural to have both implementations in the same `Serialize` function. The canonical `if(ar.IsStoring()) {...} else {...}` doesn't add incomprehensible complexity. – IInspectable May 08 '16 at 21:45
  • @IInspectable - that's a rationalization, not a good reason. What do you do when you need to write two applications, one that streams out data and one that receives it? You've got code in both that isn't used because writing and reading were merged. This goes against at least forty years of design experience that dictates minimizing coupling. – Pete Becker May 09 '16 at 14:17
  • @PeteBecker: Serialization and de-serialization are **intrinsically** coupled. This is not anything you can decouple, ever. Your example of two applications, one of which cannot read, and the other cannot write data, is contrived, particularly with MFC serialization. Among other things, MFC serialization is used for deep copying of objects. I'd be somewhat surprised, if an application stopped providing support for deep copying, because it wasn't designed to stream out data, or receive data. – IInspectable May 09 '16 at 14:29
  • @TomTom: You misinterpret MFC's implementation. With MFC, it's **not** the stream that distinguishes between reading and writing. It's the `CArchive` that does. The `CArchive` owns a stream object used for serialization, but it is never used to distinguish between streaming out or receiving data. – IInspectable May 09 '16 at 14:33
  • @IInspectable - indeed, bad design decisions propagate. – Pete Becker May 09 '16 at 14:36

2 Answers2

2

The file streams do not store any information about how they are opened and, correspondingly, can't be queried for the mode they are in. The background is that information isn't needed by the stream implementation itself and it would require to store unnecessary data. Also, when using a stream it is normally clear whether it is read or written: the operations are substantially different that the use indicates which operation is used.

If you really need to get this information, I'd recommend creating a stream type which sets up an std::iostream upon construction using a std::filebuf and stores the information in a pword() so the information can be recovered while passing the stream as std::iostream. The basics could look something like this:

#include <fstream>
#include <iostream>

struct astream_base {
    astream_base(std::string const& name, std::ios_base::openmode mode)
        : d_sbuf() {
        this->d_sbuf.open(name.c_str(), mode);
    }
    std::filebuf d_sbuf;
};

std::ios_base::openmode mode(std::iostream& stream);
class astream
    : private virtual astream_base
    , public std::iostream
{
    static int index() { static int rc = std::ios_base::xalloc(); return rc; }
public:
    astream(std::string const& name, std::ios_base::openmode mode)
        : astream_base(name, mode)
        , std::ios(&this->d_sbuf)
        , std::iostream(&this->d_sbuf) {
        this->iword(index()) = mode;
    }
    friend std::ios_base::openmode mode(std::iostream& stream) {
        return std::ios_base::openmode(stream.iword(index()));
    }
};

void print_mode(std::iostream& s) {
    std::cout << "mode=" << mode(s) << "\n";
}

int main()
{
    astream sin("test1", std::ios_base::in);
    astream sout("test2", std::ios_base::out);
    print_mode(sin);
    print_mode(sout);
}

The astream_base is primarily needed to make sure the stream's stream buffer outlives the stream operations. In particular, the stream buffer needs to be alive when the std::ostream's destructor is called as that tries to call pubsync() to flush the stream buffer. Since std::ios is a virtual base of std::istream and std::ostream, the astream_base also needs to be a virtual base.

Other than that, the astream simply associates the used open mode with the std::iostream using an iword(). The function mode() can then be used to determine the value associated. If the stream wasn't open by an astream, the mode will be zero. If you want to support using other streams you could also allow setting the mode() flag which, however, becomes a bit less reliable.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

you should set a variable for open mode. check this code:

    fstream PatternFile;
    ios_base::openmode currentOpenMode = ios_base::out;
    strFile.Format(_T("yourFile.txt"));
    PatternFile.open(strFile, currentOpenMode);
    if(PatternFile.is_open())
    {
        if(currentOpenMode  == ios_base::out)
        {
            // bingo
        }
    }
Farhad
  • 4,119
  • 8
  • 43
  • 66
  • That's not suitable for me. The class has only a reference of a stream object, he does not known anaything about a separate variable. – Tom Tom May 08 '16 at 19:18