tags:

views:

1130

answers:

7

In ANSI C++, how can I assign the cout stream to a variable name? What I want to do is, if the user has specified an output file name, I send output there, otherwise, send it to the screen. So something like:

ofstream outFile;
if (outFileRequested) 
    outFile.open("foo.txt", ios::out);
else
    outFile = cout;  // Will not compile because outFile does not have an 
                     // assignment operator

outFile << "whatever" << endl;

I tried doing this as a Macro function as well:

#define OUTPUT outFileRequested?outFile:cout

OUTPUT << "whatever" << endl;

But that gave me a compiler error as well.

I supposed I could either use an IF-THEN block for every output, but I'd like to avoid that if I could. Any ideas?

+3  A: 

You can find a very detailed explanation of how to do this here: http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81?pli=1

Hopefully someone will write this up more clearly for stack overflow to take the points...

Douglas Mayle
+23  A: 

Use a reference. Note that the reference must be of type std::ostream, not std::ofstream, since std::cout is an std::ostream, so you must use the least common denominator.

std::ofstream realOutFile;

if(outFileRequested)
    realOutFile.open("foo.txt", std::ios::out);

std::ostream & outFile = (outFileRequested ? realOutFile : std::cout);
Adam Rosenfield
That is a very elegant solution. Thanks!
A: 

I think Adam's on the right track but I don't think you can assign references - you need to use a pointer instead:

std::ofstream realOutFile;
std::ostream * poutFile;

if(outFileRequested)
{
    realOutFile.open("foo.txt", std::ios::out);
    poutFile = &realOutFile;
}
else
    poutFile = &std::cout;

you could then define a reference to be the value of the pointer, but it wouldn't be global

std::ostream & outFile = *poutFile;
KenE
Should always prefer to use references over pointers. And it can be done with references. See the fixed code above.
Martin York
Sure, you just can't use a global variable with a reference b/c you won't be able to initialize to the right value.
KenE
A: 

I'm not sure that you can assign cout to a variable of type ofstream. cout is an object of type ostream (whereas cin is of type istream) and I'm not sure that one inherits from the other. So, maybe something checking to see if a file was given/exists and creating the appropriate stream type would be a better approach.

+1  A: 

Following Adam Rosenfield's tracks, but fixing the reference initialization problem with ternary and comma operators:

bool outFileRequested = false;

std::ofstream realOutFile;
std::ostream & outFile = outFileRequested
 ? realOutFile.open("foo.txt", std::ios::out), realOutFile
 : std::cout;

outFile << "some witty remark";

(Tested in VS)

Ates Goral
+6  A: 

I assume your program behaves like standard unix tools, that when not given a file will write to standard output, and when given a file will write into that file. You can redirect cout to write into another stream buffer. As long as your redirection is alive, everything written to cout is transparently written to the destination you designated. Once the redirection object goes out of scope, the original stream is put and output will write to the screen again:

struct CoutRedirect { 
    std::streambuf * old; 
    CoutRedirect():old(0) {
        // empty
    }

    ~CoutRedirect() {
        if(old != 0) {
            std::cout.rdbuf(old);
        }
    }

    void redirect(std::streambuf * to) {
        old = std::cout.rdbuf(to);
    }
}

int main() {
    std::filebuf file;
    CoutRedirect pipe;
    if(outFileRequested) {
        file.open("foo.txt", std::ios_base::out);
        pipe.redirect(&file);
    }
}

Now, cout is redirected to the file as long as the pipe is alive in main. You can make it more "production ready" by making it non-copyable, because it's not ready to be copied: If the copy goes out of scope, it would restore the original stream already.

Johannes Schaub - litb
I think this struct should be a singleton.
Hosam Aly
Hosam, it's purposely not a singleton, to allow temporarily changing the destination of cout, a singleton would change cout forever. even if you don't want to change it only temporarily, if you can do something without a singleton, you should do it without. Here, i think it can clearly done without
Johannes Schaub - litb
A: 

This took about two hours to get. Basically, I have a external class running a test suite. I send in a delegate to run the tests, so in order to have access to output I need to send in an output stream. I guess I could have done a different stream per test. Anyways, I wanted to pass in the ofstream to be used later.

// Main code to create Test Suite Object
ofstream debugFile("debug.txt");
TestSuiteObject* myTestSuite = new TestSuiteObject(&debugFile);

// Test Suite Object
class TestSuiteObject: public Test::Suite
{
public:
 TestSuiteObject(std::ofstream* debug) : m_debug(*debug)
 {
  m_debug << "some witty remark" << std::endl;
  TEST_ADD(TestSuiteObject::test1);
  TEST_ADD(TestSuiteObject::test2);
  TEST_ADD(TestSuiteObject::test3);

 }

 void test1();
 void test2();
 void test3();

private:
 std::ofstream& m_debug;
};
sizu
Are you sure that you've put this in the right place? It does not seem to be responsive to the question...
dmckee