Присвоение cout имени переменной - PullRequest
14 голосов
/ 09 января 2009

В ANSI C ++, как я могу назначить поток cout для имени переменной? Я хочу, чтобы, если пользователь указал имя выходного файла, я отправил туда вывод, иначе отправлю его на экран. Так что-то вроде:

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;

Я также пытался сделать это как функцию макроса:

#define OUTPUT outFileRequested?outFile:cout

OUTPUT << "whatever" << endl;

Но это также дало мне ошибку компилятора.

Я предполагал, что мог бы использовать блок IF-THEN для каждого выхода, но я бы хотел избежать этого, если бы мог. Есть идеи?

Ответы [ 7 ]

36 голосов
/ 09 января 2009

Используйте ссылку. Обратите внимание, что ссылка должна иметь тип std::ostream, а не std::ofstream, поскольку std::cout является std::ostream, поэтому необходимо использовать наименее общий знаменатель.

std::ofstream realOutFile;

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

std::ostream & outFile = (outFileRequested ? realOutFile : std::cout);
8 голосов
/ 09 января 2009

Я предполагаю, что ваша программа ведет себя подобно стандартным инструментам Unix, что, если не дано, файл будет записывать в стандартный вывод, а когда ему дают файл, будет записывать в этот файл. Вы можете перенаправить cout для записи в другой потоковый буфер. Пока ваше перенаправление живо, все, что записано в cout, прозрачно записывается в указанное вами место назначения. Как только объект перенаправления выходит из области видимости, исходный поток помещается, и вывод снова выводит на экран:

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);
    }
}

Теперь cout перенаправляется в файл, пока канал работает в main. Вы можете сделать его более «готовым к производству», сделав его не подлежащим копированию, поскольку он не готов к копированию: если копия выходит за пределы области, она уже восстановит исходный поток.

3 голосов
/ 09 января 2009

Вы можете найти очень подробное объяснение того, как это сделать, здесь: http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81?pli=1

Надеюсь, кто-то напишет это более четко для переполнения стека, чтобы взять очки ...

1 голос
/ 09 января 2009

После дорожек Адама Розенфилда , но исправление проблемы инициализации ссылки с троичными и запятыми операторами:

bool outFileRequested = false;

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

outFile << "some witty remark";

(Проверено в VS)

0 голосов
/ 11 сентября 2010

Это заняло около двух часов. По сути, у меня есть внешний класс, запускающий набор тестов. Я отправляю делегата для запуска тестов, поэтому для того, чтобы получить доступ к выводу, мне нужно отправить в выходной поток. Я думаю, я мог бы сделать другой поток за тест. Во всяком случае, я хотел передать в ofstream для последующего использования.

// 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;
};
0 голосов
/ 09 января 2009

Я не уверен, что вы можете назначить cout переменной типа ofstream. cout - это объект типа ostream (тогда как cin имеет тип istream), и я не уверен, что одно наследуется от другого. Поэтому, возможно, что-то, проверяющее, был ли файл задан / существует, и создание соответствующего типа потока, было бы лучшим подходом.

0 голосов
/ 09 января 2009

Я думаю, что Адам на правильном пути, но я не думаю, что вы можете назначить ссылки - вам нужно вместо этого использовать указатель:

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

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

тогда вы можете определить ссылку как значение указателя, но она не будет глобальной

std::ostream & outFile = *poutFile;
...