пытаюсь написать std:out и файл одновременно

Я пытаюсь одновременно писать в файл и стандартный вывод в С++, перегружая поток

тест.ч

 #pragma once 

#include <iostream>

using  std::ofstream;

class OutputAndConsole:public ofstream
{
public:
    std::string fileName;        
    OutputAndConsole(const std::string& fileName):ofstream(fileName),fileName(fileName){
    };
    template <typename T>
    OutputAndConsole& operator<<(T var);
};


template <typename T>
OutputAndConsole& OutputAndConsole::operator<<(T var)
{
    std::cout << var;
    ofstream::operator << (var);
    return (*this);
};

test.cpp

  OutputAndConsole file("output.txt");
  file << "test" ;

Вывод в файле

01400930

а в консоли есть

test

Я отладил код, похоже, что он входит в

_Myt& __CLR_OR_THIS_CALL operator<<(const void *_Val)

Что я делаю не так?


person adam    schedule 02.12.2012    source источник
comment
Вам не нужно этого делать. Cout и у файла будут разные потоки, и что это...   -  person Rivasa    schedule 02.12.2012
comment
@Link: он наследует файловый поток и пытается вызвать << для базы.   -  person Lightness Races in Orbit    schedule 02.12.2012


Ответы (2)


Проблема

ofstream::operator << (var);

Это ваше использование ofstream::operator<< в качестве квалифицированного вызова функции. Вы требуете, чтобы функция поиска нашла функцию-член ofstream; наилучшее совпадение, которое является членом, - это void*, тогда как специализация для char*, которая печатает фактическое содержимое строки, является свободной функцией (т. е. не функцией-членом).

Вы обнаружите ту же проблему, если сделаете это и с cout:

std::cout.operator<<(var);

Решение

Это может сделать это:

static_cast<ofstream&>(*this) << var;

потому что вы по-прежнему используете обычный синтаксис оператора (со всем вытекающим из этого разрешением перегрузок), но делаете это с ofstream в качестве операнда LHS.

Хотя на самом деле я его не тестировал.

Вывод

Кроме того, ваша operator<< тоже должна быть бесплатной функцией, чтобы соответствовать этому соглашению.

So:

struct OutputAndConsole : std::ofstream
{
    OutputAndConsole(const std::string& fileName)
       : std::ofstream(fileName)
       , fileName(fileName)
    {};

    const std::string fileName;
};

template <typename T>
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var)
{
    std::cout << var;
    static_cast<std::ofstream&>(strm) << var;
    return strm;
};

Я также позволил себе внести небольшие изменения в синтаксис.

person Lightness Races in Orbit    schedule 02.12.2012
comment
Я считаю, что последней функцией должна быть template <typename T> OutputAndConsole& operator<<(OutputAndConsole& os, const T& var) { std::cout << var; static_cast<std::ofstream&>(os)<< (var); return (os); };. Вы правы, я атаковал функцию-член, а не глобальную - person adam; 02.12.2012
comment
Прежде всего спасибо за хорошее решение. По какой-то причине, чтобы заставить его работать (до С++ 11), мне пришлось изменить std::ofstream(fileName) на std::ofstream(fileName.c_str()). Моя проблема заключается в том, что на консоли любые std::endl и \n, кажется, игнорируются (в то время как в файле переносы строк в порядке). У вас есть идеи, почему это так? - person 463035818_is_not_a_number; 28.06.2016
comment
@ tobi303: C++11 представил конструкторы fstream, принимающие std::string, так что это неудивительно. Что касается вашей консоли, вам придется обратиться в службу технической поддержки вашего эмулятора терминала. - person Lightness Races in Orbit; 28.06.2016
comment
Для меня (MinGW-w64, Wiindows 7, работающий в Eclipse) сброс не работает, используя это. - person xamid; 05.11.2017
comment
@xamid: Что это значит, сброс не работает? - person Lightness Races in Orbit; 05.11.2017
comment
Это значит, что с помощью << std::flush или метода flush() не писать в файл (и тем самым очищать поток вывода), а надо. Единственный рабочий подход, который я нашел до сих пор, — это использование boost::iostream::tee_device<ostream, ofstream> библиотеки Boost. - person xamid; 07.11.2017
comment
@xamid: вы должны опубликовать свою проблему как новый вопрос, обязательно включив минимально воспроизводимый пример, который воспроизводит проблема. На первый взгляд, нет причин для такого исхода, хотя, возможно, есть что-то еще, что вам нужно сделать для манипуляторов ввода-вывода, что я упустил. Но вы не предоставили даже почти достаточно подробностей, чтобы правильно диагностировать проблему. - person Lightness Races in Orbit; 08.11.2017
comment
Я уже решил проблему, используя рабочий код из Boost. Я только упомянул, что ваш код неисправен, так как он не работает должным образом в Windows 7, MinGW 6.4, в то время как есть код, который работает правильно. Если вас не интересует правильность вашего конкретного кода, то зачем мне? - person xamid; 08.11.2017
comment
@xamid: Если бы меня не интересовала правильность кода, зачем бы я дважды просил вас лучше объяснить, в чем проблема с ним? - person Lightness Races in Orbit; 09.11.2017
comment
На мой взгляд, этот тест не воспроизводит вашу предполагаемую проблему (сложно воспроизвести промывку проблемы со 100% уверенностью, но добавление/удаление << std::flush из строки 35 - довольно четкий тест) - person Lightness Races in Orbit; 09.11.2017

Я не буду комментировать, почему ваш подход не работает, в основном потому, что его нельзя исправить, чтобы он работал должным образом. Основная проблема заключается в том, что вы не можете использовать свой поток, чтобы передать его чему-то, что ожидало std::ostream&, и при этом писать в оба потока. Однако существует относительно простой, хотя и не обязательно очевидный подход к реализации того, что вы на самом деле хотите: вы должны создать новый буфер потока, т. е. класс, производный от std::streambuf, и переопределить его функции overflow() и sync(). Вот полный код простой демонстрации:

#include <streambuf>

struct teebuf
    : std::streambuf
{
    std::streambuf* sb1_;
    std::streambuf* sb2_;

    teebuf(std::streambuf* sb1, std::streambuf* sb2)
        : sb1_(sb1), sb2_(sb2) {
    }
    int overflow(int c) {
        typedef std::streambuf::traits_type traits;
        bool rc(true);
        if (!traits::eq_int_type(traits::eof(), c)) {
            traits::eq_int_type(this->sb1_->sputc(c), traits::eof())
                && (rc = false);
            traits::eq_int_type(this->sb2_->sputc(c), traits::eof())
                && (rc = false);
        }
        return rc? traits::not_eof(c): traits::eof();
    }
    int sync() {
        bool rc(true);
        this->sb1_->pubsync() != -1 || (rc = false);
        this->sb2_->pubsync() != -1 || (rc = false);
        return rc? 0: -1;
    }
};

#include <fstream>
#include <iostream>

int main()
{
    std::ofstream fout("tee.txt");
    teebuf        sbuf(fout.rdbuf(), std::cout.rdbuf());
    std::ostream  out(&sbuf);
    out << "hello, world!\n";
}

Очевидно, что создание tee-stream можно красиво упаковать, но то, как это выглядит, не имеет большого значения. Важно то, что можно создать собственное место назначения (или источник) для IOStreams и что это не предполагает каких-либо попыток наследования от std::ostream. Единственная причина для наследования от std::ostream (или std::istream) состоит в том, чтобы упростить инициализацию потока с помощью пользовательского буфера потока.

person Dietmar Kühl    schedule 02.12.2012
comment
Я не знаю, в чем ошибка, но при использовании этого в Windows 7, скомпилированного с помощью MinGW-w64, у этого есть ошибка, заключающаяся в том, что после сброса fout он больше ничего не записывает в файл (через короткий промежуток времени) . Программа все еще говорит, что фоут будет открыт, но не реагирует. Когда используется только поток, он работает нормально. Интересно, что сбой не появляется при выполнении в eclipse, а когда я запускаю только .exe. - person xamid; 05.11.2017