Question

Below are two pieces of code. One works, one does not, I'd like to know why. I apologise in advance for the lack of comments and awful variable names but right now this language is really grinding my gears.

// File1.cpp (contains relevant includes)
// Works! It writes to out.txt and appears to use in.txt correctly
int main(int argc, char* argv[]) {
    int num;
    std::ifstream in("in.txt");
    std::streambuf* cinbuf = std::cin.rdbuf();
    std::cin.rdbuf(in.rdbuf());

    std::ofstream out("out.txt");
    std::streambuf* coutbuf = std::cout.rdbuf();
    std::cout.rdbuf(out.rdbuf());

    cout << "Give me a number: ";
    cin >> num;

    std::cin.rdbuf(cinbuf);
    std::cout.rdbuf(coutbuf);

    return 0;
}

// File2.cpp (contains relevant includes)
// Does not work! Outputs nothing to out.txt.
class TestWithStdIO {
        std::streambuf* cinbuf;
        std::streambuf* coutbuf;
    public:
        TestWithStdIO(const char* inFile, const char* outFile) {
            std::ifstream in(inFile);
            cinbuf = std::cin.rdbuf();
            std::cin.rdbuf(in.rdbuf());

            std::ofstream out(outFile);
            coutbuf = std::cout.rdbuf();
            std::cout.rdbuf(out.rdbuf());
        }
        ~TestWithStdIO() {
            std::cin.rdbuf(cinbuf);
            std::cout.rdbuf(coutbuf);
        }
};

int main(int argc, char* argv[]) {
    int num;
    TestWithStdIO* ioTest = new TestWithStdIO("in.txt", "out.txt");
    cout << "Give me a number: ";
    cin >> num;
    delete ioTest;

    return 0;
}
Was it helpful?

Solution

The std::[io]fstreams in and out have local scope (or automatic storage duration) inside the TestWithStdIO constructor. They are destroyed (and the files closed), along with their contained buffers, at the end of the function, leaving you with dangling pointers insidecin and cout.

Consider instead making in and out members of TestWithStdIO, something like this:

class TestWithStdIO {
        std::streambuf* cinbuf;
        std::streambuf* coutbuf;

        std::ifstream in_;  // <-- member
        std::ofstream out_; // <-- member
    public:
        TestWithStdIO(const std::string& inFile, const std::string& outFile) :
            in_(inFile), out_(outFile) // <-- initializer list
        {
            cinbuf = std::cin.rdbuf();
            std::cin.rdbuf(in_.rdbuf());

            coutbuf = std::cout.rdbuf();
            std::cout.rdbuf(out_.rdbuf());
        }
        ~TestWithStdIO() {
            std::cin.rdbuf(cinbuf);
            std::cout.rdbuf(coutbuf);
        }
};

You might need in(inFile.c_str()) and the equivalent for out if you only have C++03 support.

Also, as pointed out in the comments, there is no need to dynamically allocate the TestWithStdIO instance, and in fact it is likely to be a source of bugs. Just do

TestWithStdIO ioTest ("in.txt", "out.txt");

OTHER TIPS

In TestWithStdIO::TestWithStdIO(), The stream buffer pointed to by in.rdbuf() is destroyed together with in (i.e. at the end of the constructor).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top