опитвайки се да напиша std:out и file едновременно

Опитвам се да пиша във файл и stdout едновременно в рамките на c++ чрез претоварване на ofstream

тест.ч

 #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
Първо благодаря за хубавото решение. По някаква причина, за да работи (преди C++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: Трябва да публикувате проблема си като нов въпрос, като не забравяйте да включите минимално възпроизводим пример, който възпроизвежда проблем. На пръв поглед няма причина този резултат да се случи, въпреки че може би има нещо допълнително, което трябва да направите за I/O манипулаторите, което пропускам. Но не сте предоставили дори достатъчно подробности, за да диагностицирате правилно проблема. - 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 бъде изчистен, той няма да запише нищо повече във файла (след кратък период от време) . Програмата все още казва, че фуната ще бъде отворена, но не реагира. Когато използвате само ofstream, той работи добре. Интересното е, че грешката не се появява, когато се изпълнява в eclipse, но когато стартирам .exe сам. - person xamid; 05.11.2017