• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed wskaźnik do std::ostream problemy z flushowaniem

Object Storage Arubacloud
+1 głos
190 wizyt
pytanie zadane 25 lutego 2021 w C i C++ przez Ptaszor3 Użytkownik (900 p.)
zamknięte 26 lutego 2021 przez Ptaszor3

Mam pytanie odnośnie c++. Piszę klasę która tworzy interfejs pomiędzy konsolą i resztą kodu który napisałem. Mam klasę w której znajduje się zmienna składowa
std::ostream *outputStream;

Problem polega na tym, że czasami linijka
 

*outputStream << "This command does not exist" << std::endl;

zachowuje się, jakby outputStream nie został sflushowany mimo "std::endl" na końcu. To jest wypisuje się razem z wyjściem innych obsługi innych poleceń wczytanych z klawiatury. Co więcej nie działa również używanie funkcji "flush()".  Nie wszystkie wypisania zachowują się w taki sposób. Dotyczy on wyłącznie krótszych bloków np:
 

else                                                                   
  *outputStream << "This command does not exist" << std::endl;

zachowuje się jakby nie była flushowana. Natomiast
 

else if (arguments[0] == "setRadius") {
  try {
    findByName(arguments[1]).setRadius(std::stod(arguments[2]));
  }
  catch(std::string err_txt) {
    *outputStream << err_txt;
  }
}

działa poprawnie. Skończyły mi się pomysły na rozwiązanie tego problemu, szukałem w internecie  (najwidoczniej zbyt krótko). Wydaje mi się, że może to mieć związek z tym, że jest to wskaźnik do strumienia a nie jego referencja.

EDIT: spróbowałem jeszcze skompilować clangiem i gcc z flagą -O0. Nic nie zmienia tego zachowania. Oprócz tego, chciałbym dodać, że w komentarzach do tego pytania dodałem więcej kodu źródłowego i zawęziłem problem do jakichś czterech linijek kodu.

EDIT 2: cały problem powoduje inputStream->peek() == EOF. nie wiem czemu

komentarz zamknięcia: Problem rozwiązany
komentarz 25 lutego 2021 przez tangarr Mędrzec (154,860 p.)
W jaki sposób wywołujesz funkcję flush?
komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)

wywoływałem ją tak:
 

*outputStream.flush();

pod koniec tego if wypisującego

1
komentarz 26 lutego 2021 przez TOM_CPP Pasjonat (22,640 p.)

@Ptaszor3, Dlaczego zamiast

*outputStream << "This command does not exist" << std::endl;

nie używasz ( w zależności od sytuacji )

cout << "This command does not exist" << std::endl;

lub

cerr << "This command does not exist" << std::endl;

lub

file << "This command does not exist" << std::endl;


 

komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Dlatego, że chciałem zachować największą ogólność pisania programu. Poza tym wiem, że przydaje mi się bardzo podobna zmienna *inputStream którą mogę wczytywać polecenia z pliku. Mimo wszytsko chciałbym zachować ten wskaźnik, ale jak teraz o tym myślę, to wydaje mi się, że powinienem sprawdzić czy problem się rozwiąże jeżeli wstawię zwykłago couta zamiast tego wskaźnika.
komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Ok, to nie jest wina wskaźnika. Kiedy zamieniłem *outputStream bezpośrednio na couta dalej nie działa.
komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)

Znalazłem coś ciekawego. Po tej instrukcji warunkowej wszystkie próby wypisania zachowują się dziwnie:

else if (arguments[0] == "continue" and !isOpenedFromFile) {
                        
   *outputStream << "Continuing" << std::endl;
   return;
}

isOpenedFromFile jest ustawione kiedy polecenia wczytywane sąz pliku

komentarz 26 lutego 2021 przez TOM_CPP Pasjonat (22,640 p.)

Trudno jest powiedzieć coś więcej, bez podania minimalnej ilości kodu, potrzebnego do odtworzenia sytuacji w której występuje błąd. Minimal working example

komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)

ok, podeślę całą funkcję

void Terminal::readInput(bool isOpenedFromFile = false) {

	if(!isOpenedFromFile)
		*outputStream << "Entered command mode - program is paused" << std::endl;
	else
		*outputStream << "Started loading the file" << std::endl;

	while(true) {

		std::vector<std::string> arguments;	
		std::string input;
		std::getline(*inputStream, input);
		arguments = divideIntoWords(input);	

		if (arguments[0] == "newPlanet") {

			bool hasBeenCatched = false;
			try {
				findByName(arguments[1]);
			}
			catch(std::string) {
				universe->planets.push_back(Planet(arguments[1]));
				hasBeenCatched = true;
			}
			
			if(!hasBeenCatched)
				*outputStream << "Planet " << arguments[1] << " has already been defined" << std::endl;
			
		}
		else if (arguments [0] == "removePlanet") {

			for(std::vector<Planet>::iterator iterator = universe->planets.begin(); iterator < universe->planets.end(); iterator++)
				if(iterator->name == arguments[1]) {
					universe->planets.erase(iterator);
					break;
				}
		}
		else if (arguments[0] == "setRadius") {

			try {
				findByName(arguments[1]).setRadius(std::stod(arguments[2]));
			}
			catch(std::string err_txt) {
				*outputStream << err_txt;
			}
		}
		else if (arguments[0] == "setColor") {
 
			try {
				findByName(arguments[1]).setFillColor(stringToEnumColor(arguments[2]));
			}
			catch(std::string err_txt) {
				*outputStream << err_txt;
			}
		}
		else if (arguments[0] == "setVelocity") {
	
			try {
				findByName(arguments[1]).speed = OurVector(std::stod(arguments[2]), std::stod(arguments[3]));
			}
			catch(std::string err_txt) {
				*outputStream << err_txt;
			}
		}
		else if (arguments[0] == "setPosition") {

			try {
				findByName(arguments[1]).setPosition(std::stod(arguments[2]), std::stod(arguments[3]));
			}
			catch(std::string err_txt) {
				*outputStream << err_txt;
			}
		}
		else if (arguments[0] == "setMass") {

			try {
				findByName(arguments[1]).mass = std::stod(arguments[2]);
			}
			catch(std::string err_txt) {
				*outputStream << err_txt;
			}
		}
		else if (arguments[0] == "setG") {

			universe->G = std::stod(arguments[1]);
		}
		else if (arguments[0] == "loadFromFile") { 

			std::ifstream inputFileStream;
			inputFileStream.open(arguments[1]);

			if(inputFileStream.good()) {
				std::istream *ibuffer {inputStream};
				inputStream = &inputFileStream;
		
				readInput(true);

				inputStream = ibuffer;
				inputFileStream.close();
			}
			else 
				*outputStream << "Error has been encountered during opening \"" << arguments[1] << "\" file." << std::endl;
			
		}
		else if (arguments[0] == "listPlanets") {

			if(!universe->planets.size())

				*outputStream << "There are no planets yet! Try creating one with newPlanet" << std::endl;
			else {

				*outputStream << "Planet name \tMass \tRadius \tPositon \tVelocity" << std::endl;
				
				for(auto cPlanet : universe->planets)
					*outputStream << cPlanet.name << "\t\t" << cPlanet.mass << '\t' << cPlanet.getRadius() << "\t(" << cPlanet.getPosition().x << ", " << cPlanet.getPosition().y << ")\t\t(" << cPlanet.speed.x << ", " << cPlanet.speed.y << ')' << std::endl;
			}
		}
		else if (arguments[0] == "lala") {									//[1]to i wszystkie poprzednie elese ify działają ok

			*outputStream << "Trililalalola!" << std::endl;
			*outputStream << "Labadadibdabda!" << std::endl;
			*outputStream << "Kikikikakakipodipi!" << std::endl;
			if(arguments[1] == "Krabopająk")
			*outputStream << "Ooooooo pajajajonczemk wkromczył na scenę. Ooooooo i zjamdł " << arguments[1] << std::endl;
		}	
		else if (arguments[0] == "continue" or inputStream->peek() == EOF and isOpenedFromFile) {		//ta funkcja robi problemy
			
			*outputStream << "Instructions has been loaded" << std::endl;
			return;
		}
		else if (arguments[0] == "kaka") {									//to ma takie samo ciało jak [1] ale wypisuje się niepoprawnie

			*outputStream << "Trililalalola!" << std::endl;
			*outputStream << "Labadadibdabda!" << std::endl;
			*outputStream << "Kikikikakakipodipi!" << std::endl;
			if(arguments[1] == "Krabopająk")
			*outputStream << "Ooooooo pajajajonczemk wkromczył na scenę. Ooooooo i zjamdł " << arguments[1] << std::endl;
		}
		else if(arguments[0] == "\n"){}						//W rezultacie to wypisuje się jakby było nieflushowane
		else 
			*outputStream << "This command does not exist" << std::endl;

	}
}

Klasa Terminal
 

class Terminal {

public:
	Terminal (Universe *cUniverse, std::istream *cInputStream, std::ostream *cOutputStream);

	Universe *universe;
	std::istream *inputStream;
	std::ostream *outputStream;

	void readInput(bool isOpenedFromFile);
	std::vector<std::string> divideIntoWords(std::string textToDivide);
	sf::Color stringToEnumColor(std::string inputString);
	void toLower(std::string & inputString);
	Planet& findByName(std::string);
};

To dzieje się w terminalu

lala                                           //input

Trililalalola!                                 //output
Labadadibdabda!
Kikikikakakipodipi!

kaka                                           //input

thisIsNotAProperCommand                        //input!!!

Trililalalola!                                 //output
Labadadibdabda!
Kikikikakakipodipi!

listPlanets                                    //input

This command does not exist                    //output
There are no planets yet! Try creating one with newPlanet

Zaznaczyłem komentarzami miejsca w których dzieje się błąd.

Przepraszam, że nie wysłałem wcześniej, myślałem, że to jakiś błąd z rodzaju tych prostych do rozwiązania. :<

3 odpowiedzi

+1 głos
odpowiedź 26 lutego 2021 przez TOM_CPP Pasjonat (22,640 p.)
wybrane 26 lutego 2021 przez Ptaszor3
 
Najlepsza

Ja osobiście wywaliłbym wskaźniki do ostream / istream i zmienił na funkcje szablonowe. Dodatkowo użyłbym instrukcji switch. Całość mogła by wyglądać ja poniżej.

#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <map>

using namespace std;

enum class planet { earth , mars , saturn , jupiter };

const map<string,planet> mplanet { {"earth",planet::earth},{"mars",planet::mars},
                                   {"saturn",planet::saturn},{"jupiter",planet::jupiter} };

class Terminal
{
    template< istream& in = cin >
    vector<string> readInput() const
    {
        string input;
        getline(in,input);
        return split(input);
    }

    auto split( const string& input ) const
    {
        istringstream buffer(input);
        return vector<string>{istream_iterator<string>(buffer),istream_iterator<string>()};
    }

public:

    template< ostream& out = cout >
    void process()
    {
        auto argument = readInput();
        if( argument.size() == 0 ) return;
        if( mplanet.count(argument[0]) == 0 ){ out << "Cannot find this planet" << endl; return; }

        switch( mplanet.at(argument[0]) )
        {
            case planet::earth:
                   out << "We are on Earth" << endl;
                   if( argument.size()>1 )
                   {
                       out << "flying with " << argument[1] << endl;
                   }
                 break;
            case planet::mars:
                   out << "We are on Mars" << endl;
                 break;
            case planet::saturn:
                   out << "We are on Saturn" << endl;
                 break;
            case planet::jupiter:
                   out << "We are on Jupiter" << endl;
                 break;
        }
    }
};

int main()
{
   Terminal terminal;

   cout << "Enter arguments : " << endl;
   terminal.process();  
}

https://wandbox.org/permlink/CgOIQic7OX9VdEmq

komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Napewno wygląda to lepiej i jest bardziej obiektowe, na pewno by działało ale zależy mi na dowiedzeniu się czemu ten parszywy program nie działa tak jak powinien. Dam najlepsze i tak zrobię jeżeli nie dowiem się jak to zrobić z tymi wskaźnikami.
0 głosów
odpowiedź 26 lutego 2021 przez j23 Mędrzec (194,920 p.)

Daj na początku std::ios::sync_with_stdio(true);

komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Nie działa, wstawiłem w maine, a później spróbowałem wsadzić do konstruktora Terminal. Nic nie zmienia :\
komentarz 26 lutego 2021 przez j23 Mędrzec (194,920 p.)

Jeszcze jedną rzecz zrób: inputStream->tie(outputStream);

komentarz 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Dałem std::ios::sync_with_stdio(true) na początku maina i inputStream->tie(outputStream) w konstruktorze Terminal i dalej nie działa :<
0 głosów
odpowiedź 26 lutego 2021 przez Ptaszor3 Użytkownik (900 p.)
Dzień dobry! Problem został rozwiązany. Przeniosłem sprawdzanie, czy nie nastąpił EOF na początek pętli. Nie wiem czemu ale mam wrażenie, że peek() zamraża output stream jeżeli żaden znak nie znajduje się w streamie. Jeżeli ktoś ma ciekawą teorię, czemu tak się dzieję chętnie jej posłucham w komentarzu. Zamknę pytanie po kilku dniach, ewentualnie jeżeli znajdę gdzieś rozwiązanie, lub ktoś odpowie.
komentarz 26 lutego 2021 przez Michał Muzyka Pasjonat (24,080 p.)
else if (arguments[0] == "continue" or inputStream->peek() == EOF and isOpenedFromFile) {        //ta funkcja robi problemy
       
            *outputStream << "Instructions has been loaded" << std::endl;
            return;
 }

//zmienić na

else if (arguments[0] == "continue" or inputStream->peek() == EOF and isOpenedFromFile) {        //ta funkcja robi problemy
            if(inputStream->peek() == EOF)
                 inputStream->ignore(); // lub get()

            *outputStream << "Instructions has been loaded" << std::endl;
            return;
 }

peek() podgląda tylko to co znajduje się aktualnie w buforze nie czyszcząc go, czyli sprawdzasz, że w buforze znajduje się znak EOF i go tam zostawiasz, czyli co każdy obrót pętli warunek tego ifa jest spełniony, musisz wyczyścić bufor lub pobrać ten znak.

Podobne pytania

0 głosów
3 odpowiedzi 1,280 wizyt
pytanie zadane 2 maja 2017 w C i C++ przez Pajdas Mądrala (5,930 p.)
0 głosów
2 odpowiedzi 542 wizyt
pytanie zadane 27 grudnia 2019 w C i C++ przez Sejdi Początkujący (460 p.)
0 głosów
1 odpowiedź 209 wizyt

92,632 zapytań

141,500 odpowiedzi

319,879 komentarzy

62,013 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...