Maksymalna ilość znaków w strumieniu

0

Cześć, próbuję sobie robić zadania z książki Thinking in C++, jednak utknąłem na podstawach strumieni.
Czy ktoś może mnie naprowadzić na rozwiązanie?

Zadanie brzmi:

Wiemy, że setw() umożliwia wczytanie minimalnej liczby znaków, ale co, jeśli chcemy odczytać liczbę maksymalną? Napisz efektor, który umożliwi użytkownikowi wskazać maksymalną liczbę znaków do odczytania. Niech efektor ten działa także w przypadku strumieni wyjściowych, skracając w miarę potrzeb pola tak, aby nie wykraczały poza wyznaczoną długość.

Wiem, że muszę stworzyć klasę z konstruktorem, który przyjmie w parametrze ilość znaków. Dodatkowo ta klasa musi przeciążać operatory << i >>.
Niestety niczego więcej nie udało mi się osiągnąć. Jakieś wskazówki?

1

Czyli nie zrozumiałeś co robi setw :/

0

Wydaje mi się, że rozumiem, co robi setw. Niemniej jednak w żaden sposób mi to nie pomaga. Próbowałem kombinować z ustawieniem width, ale niczego to nie dało.

1

Racja, wydaje Ci się. Na początek zobacz czym jest setw.

0

setw to efektor (manipulator z parametrem), który robi to samo, co metoda strumienia: width. Efekty tych linijek będą identyczne:

 
//Przypadek 1 z użyciem metody
cout.width(5);
cout << "Cze";

//Przypadek 2 z użyciem efektora
cout << setw(5) << "Cze";

Co jeszcze skrywa w sobie ten setw?

0

Serio nikt nie potrafi mi pomóc? Więc ponownie proszę o przeniesienie tematu na forum C++.

0

Może przeczytaj ten rozdział raz jeszcze, tylko uważnie.

0

No czytałem już 2 razy i nadal nie wiem, czego nie łapię. Czytam też w necie, ale nadal nic.

5

Gdybyś kliknął F2, F4 czy co odpowiada w Twoim IDE za "idź do definicji" to byś zobaczył, że std::setw jest funkcją. Gdybyś sprawdził to w dokumentacji: http://en.cppreference.com/w/cpp/io/manip/setw - zobaczyłbyś to samo. Tyle.

Masz tu przykładową implementację dla limitowanego wyjścia - ilość znaków ustawiłem jako stałą kompilacji, żebyś też mógł coś sam zrobić.
http://melpon.org/wandbox/permlink/GfsUWUMrIQB7JuqF

0

Nie, no nie ogarniam tego w żaden sposób.
W książce mi piszą, że muszę stworzyć klasę, która przeciąża operator <<.
Ten rozdział jest jeszcze przed template'ami, więc to musi być proste. Kombinuje na wszystkie możliwe sposoby i dochodzę do wniosku, że coś jest nie tak, ale nie wiem co.

 
class setmax
{
public:
	setmax(int i): m_i(i)
	{

	}

	friend ostream & operator << (ostream & s, const setmax & effector);
private:
	int m_i;
};

ostream & operator << (ostream & os, const setmax & effector)
{
//dla testow, czemu string jest pusty po tej operacji?
	stringstream ss;
	ss << os.rdbuf();
	string str = ss.str();

	return os;
}



int _tmain(int argc, _TCHAR* argv[])
{	
	cout << setmax(5) << "Maksimum 5 znakow";

	getchar();
	return 0;
}

Mój problem polega na tym, że nie wiem, co zrobić w tym operatorze.
Pytanie na początek - czemu string jest pusty po tej operacji?
Jeśli zamiast

 
ss << os.rdbuf();

dam

 
ss.copyfmt(os);

to string też jest pusty.

1

A jaki ma być? Przecież to jest strumień wyjściowy.

Próbujesz metodą dodawania i odejmowania losowego kodu uzyskać działający program. Obawiam się, że w rozsądnym czasie nie jest to możliwe.

Gdybyś zechciał przeanalizować mój kod wiedziałbyś, że:

  1. zwracam nowy, opakowany stream (max_width_ostream), który wie/pamięta jaka jest maksymalna długość następnego argumentu i stosuje się do tego w swoim operatorze<<.
  2. maxw jest funkcją (ok, szablonem), która dostaje maksymalną długość jako parametr i tworzy obiekt adaptujący stream do max_width_ostream.
  3. przy dynamicznym ustalaniu maksymalnej długości możesz od razu tworzyć opakowany stream.
0
  1. Nie za bardzo mogę zrozumieć Twój kod.
  2. OK, strumień wyjściowy. Zgadza się. To dlaczego tutaj też dostaję pustego stringa?
 
istream is(os.rdbuf());
string str;
getline(is, str);
0

Czym jest os?

0

Sorry, myślałem, że ogarniesz z mojego poprzedniego postu ;) To oczywiście przekazany w parametrze ostream.

1

Okej, w takim razie co spodziewałeś się wczytać z streamu wyjściowego? Wiesz dlaczego musiałeś tak kombinować poprzez przekazywanie rdbuf, żeby to się w ogóle skompilowało? Bo do streamów wyjściowych się zapisuje, a z wejściowych wczytuje.

0

OK, ale wg Thinking in C++:

Jednak jednym z najciekawszych zastosowań wskaźnika streambuf jest łączenie go z innym obiektem iostream za pomocą operatora <<. Powoduje to przekazanie wszystkich znaków jednego obiektu do obiektu będącego lewym argumentem operatora <<.

Tom II, strona 147

Poniższy kod najpierw tworzy ifstream z flagami pozwalającymi czytać i pisać do pliku. Nie można oczywiście pisać do ifstream, więc trzeba stworzyć odpowiedni ostream z tym samym buforem:

 
ifstream in("nazwapliku", ios::in | ios::out);
ostream out(in.rdbuf());

Po ponownym przeczytaniu rodzi mi się kolejny wniosek. Można utworzyć ostream na podstawie istream, ale nie na odwrót? Wobec tego jak mam się dobrać do tego, co jest w ostream i ograniczyć mu ilość znaków?

1

A dlaczego np. nie spojrzysz na przykład, który dałem kilka postów temu i który działa?

Przykład z książki bardzo wyraźnie tworzy stream dwukierunkowy. ostream z definicji taki nie jest, więc o ile nie podajesz tam streamu wyjściowego z którego możesz czytać to Twoje rozwiązanie jest bez sensu. W kontekście tego zadania jest zupełnie bez sensu. Nie masz zamieniać streamu wyjściowego w wejściowy i odwrotnie, masz tylko ograniczyć ilość znaków następnego argumentu - wejściowego dla streamu wejściowego i wyjściowego dla streamu wyjściowego.

0

Prześledźmy to:

template<typename T>
	ostream& operator<<(T const& value){
		std::basic_stringstream<CharT, Traits> msg;
		msg.copyfmt(stream);
		msg << value;
		return stream << msg.str().substr(0, width);
	}

Najpierw tworzysz stringstream i kopiujesz do niego ustawienia ostream bez zawartości bufora.
Następnie wpisujesz do niego... value. Czym jest value? Nie potrafię tego przeanalizować w tym kodzie.

0

Dowolnym prawym argumentem operatora<< (pomijając wskaźniki na funkcje zdefiniowane niżej). int, string, cokolwiek.

0

I tu jest problem. Bo u mnie tym argumentem jest ostream, jak wynika z konstrukcji operatora <<.

0

Chyba Ci się strony pomyliły.
Strzałka w lewo: ⟵
Strzałka w prawo: ⟶

0

No nic mi się nie pomyliło, poza tym, że napisałem "<" zamiast "<<" :) Żeby przeciążyć operator << dla ostream, muszę zwrócić referencję do ostream, a w pierwszym parametrze przyjąć też referencję do ostream. Dlatego nie wiem, dlaczego u Ciebie jest ten np. string.

0

Pisałem, że jest prawym argumentem.

lewy << prawy

W kodzie, o który pytałeś nie przeciążam operatora<< dla ostream, tylko definiuję go dla mojego typu max_width_ostream. Lewym argumentem jest ten właśnie typ. Inaczej, obawiam się, się nie da (pomijając jakieś chore kombinacje).

0

W zakamarkach książki znalazłem fragment kodu, który chyba ma realizować ten problem. Niestety opiera się na pewnym oszustwie:

class setmax
{
public:
	setmax(const string & str, int i): 
		m_str(str, 0, i)
		,m_i(i)
	{

	}

	friend ostream & operator << (ostream & s, const setmax & effector);
private:
	int m_i;
	string m_str;
};

ostream & operator << (ostream & os, const setmax & effector)
{
	return os << effector.m_str;
}

int _tmain(int argc, _TCHAR* argv[])
{
	string str = "Maksimum 5 znakow";
	ostringstream os;
	os << setmax(str, 5);

	getchar();
	return 0;
}

Teraz mam tylko pytanie. Dlaczego w takiej postaci działa, ale jak użyję cout zamiast ostringstream to się wywala?

0

Jak nie działa? http://melpon.org/wandbox/permlink/uvAp6p4nWkFBI9Qf

Inna sprawa, że gdybym to ja był wykładowcą to bym takiego rozwiązania nie przyjął, bo to nie jest rozwiązanie zadania.

0

OK, nie działało zapewne przez to:

_setmode(_fileno(stdout), _O_U16TEXT);

Miałem to na początku.

Wg mnie też to nie jest rozwiązanie zadania, ale uważam też, że zadanie nie zostało dobrze sformułowane. I faktycznie chyba nie da się inaczej niż tak, jak Ty to zrobiłeś.

1 użytkowników online, w tym zalogowanych: 0, gości: 1