c++ streams
DESCRIPTION
C++ Streams. Input og Output med C++ Standard Biblioteket. Sammenheng. Hva gjør Streams? Input/output Formattering av tall, datoer, etc. Utskrift av objekter Transparent håntering av forskjellige kilder Hvilken annen funksjonalitet finnes for dette? printf, wfprintfn, tsprintf, etc. - PowerPoint PPT PresentationTRANSCRIPT
Sammenheng
• Hva gjør Streams?– Input/output
– Formattering av tall, datoer, etc.
– Utskrift av objekter
– Transparent håntering av forskjellige kilder
• Hvilken annen funksjonalitet finnes for dette?– printf, wfprintfn, tsprintf, etc.
– strftime
– scanf
Hva gir Streams?
• Uniform håntering av argumenter• Typesikkerhet• Transparent håntering av kilde og destinasjon
– Konsol I/O– Streng– Fil– Egendefinerte (f.eks. Nettverk, eller DebugStream)
• Automatisk minnehåntering/bufferhåntering• Intergrasjon med Locales
Uniform håntering av argumenter
{MyClass obj;struct tm timeNow;char const* myString;int myNumber;
std::cout << obj << ” litt tekst ” << timeNow
<< ”mer tekst ” << myString << myNumber
<< std::endl;}
Typesikkerhet
printf("%d\n", int_variable); // correctprintf("%f\n", double_variable); // correct
printf("%d\n", double_variable); // outputting an int, but a double value is supplied
printf("%f\n", int_variable); // outputting a double, but an int value is supplied
printf(”%s\n”, string(”Hello world”));// outputting a string, but an object is supplied
scanf("%d", &int_variable); // correctscanf("%d", int_variable); // oops! left out the ampersand!
scanf("%lf", &int_variable); // oops! reading a floating-point value into an integer!
scanf(”%s”, &string_variable); // oops! reading into object
Kildeuavhengighet
• Gjøres ved hjelp av stream-buffer klasser• En istream/ostream kan assosieres med en vilkårlig
stream-buffer klasse• Dersom ingen buffer-klasse passer, kan man lage
sin egen• fstream- og stringstream-klassene bruker kun
constructor til å angi en fil-buffer• En streambuffer hånterer minne selv• En stream brukes på samme måte uavhengig av
buffer
Eksempel
{std::ofstream logFile(”LogFile.txt”);
streambuf oldBuf = cout.rdbuf(logFile.rdbuf());
// Bruk cout som normal. Output vil komme// til LogFile.txt fila
// Reset buffercout.rdbuf(oldBuf);
}
operator<< og operator<<
• Operator<< brukes til å skrive ut verdier
• Operator>> brukes til å lese verdier
ofstream os(”outfile.txt”);ifstream is(”infile.txt”);
int a = 14, b = 20;string str(”Hello world”);
os << a << ” + ” << b << ” = ” << a+b << endl;os << str << endl;
is >> a >> b >> b;
Operator overloading
• << og >> er overloaded for alle argumenter vi ønsker å skrive ut
• << og >> har presendens mellom +/- og <, <=, etc– os << a + b os << (a + b), men– os << a < b (os << a) < b, som er feil
• << er venstre-assosiativ– os << a << b << c ((os << a) << b) << c
op<<(op<<(op<<(os, a), b), c)
– Dette fungerer fordi op<<(os, a) returnere en referanse til os
Definere egne operatorer
• Man kan overloaded operator<< og operator>> for å lage lesing og skriving av en klasse.
Eksempel: VARIANT
1.wostream& operator<<(wostream& os, VARIANT const& arg) {2. CComVariant var(arg); // Lager en kopi vi kan endre3. os << L"("; // Skriver ut ’(’ på begynnelsen4. 5. if ( var.vt & VT_ARRAY ) { // Kaller ny funk. for array6. PrintArray(var.vt, var.parray, os);7. } else if ( var.vt == VT_RECORD ) {8. PrintRecord(var, 0, os); // Kall ny funk for record9. } else if ( FAILED(var.ChangeType(VT_BSTR)) ){10. os << L"<nonprintable>"; // Ingen behandling11. } else if ( var.bstr == NULL ) {12. os << _T(”’’”); // Tom streng13. } else { // Vi har nå konvertert verdien til en bstring14. os << L"'" << var.bstr << L"'";15. }16. os << _T(" [") << VarTypeToString(arg.vt) << _T("])");17. return os;18.}
Manipulatorer
• I tillegg til tekst, kan man sende manipulatorer til en strøm
• Manipulatorer endrer ofte oppførsel til strømmen, eller har andre sideeffekter
• Eksempler på manipulatorer fra <iomanip>– endl – skriver ut en newline og flusher buffer– Setw, setfill – setter bredden og fill for numre– Boolalpha – skriver ”true” og ”false” for bool– Setprecision – setter presisjon for flyttall– Fixed, scientific – setter notasjon for flyttall
Output formattering
• Integers:– os << setw(4) << setfill(’0’) << 8 ”0008”
– os << setbase(16) << showbase << 2147500037 ”0x80004005” (E_FAIL)
• Bools– os << boolalpha << true ”true”– os << noboolalpha << true ”1”
• Floating points– os << scientific << 1000.0 ”1.00000e3”– os << fixed << setprecision(2) << PI ”3.14”
Manipulatorer
• Uten argumenter– En funksjon med profil ostream& f(ostream&)
er automatisk en manipulator– Eks: tostream& GetLastError(tostream& os);
• Med argumenter– Må være en klasse med args som constructor-
argumenter og operator << definert
Eksempel: Formatstruct Format // Uses FormatMessage API to decode error code{
Format(HRESULT hr) : m_hr(hr) {}
friend ostream& operator<<(ostream& os, Format const& self) {LPSTR lpBuf = 0;DWORD nRetval = FormatMessage(..., NULL, self.m_hr, 0,
(LPSTR)&lpBuf, 0, NULL);
if ( nRetval != 0 ) { // FormatMessage var vellykketos << lpBuf;LocalFree(lpBuf);
} else { // FormatMessage feiletos << "Error " << showbase << setbase(0x10) << self.m_hr <<
endl;}
return os;}
private:HRESULT m_hr;
};
cout << Format(E_NOTIMPL) << std::endl; // ”Not implemented”cout << Format(0x80041002) << std::endl; // ”Error 0x80041002”
Eksempel: GetLastErrorostream& GetLastError(ostream& os){
DWORD nStatus = ::GetLastError();if ( nStatus == 0 ) return os; // no error occured
os << "Error code : " << nStatus << ". ";os << "Error text : " << Format(nStatus) << std::endl;
return os;}
// Brukos << GetLastError << std::endl;
// Mangler med Format (finnes i WinStream.cpp i f.eks. AccountingSrv):// Må fjerne \n\r fra slutten av feiltekster// Må kunne finne feil i andre moduler:
string const oleDbModule("c:\\program files\\common files\\system””\\ole db\\msdaerr.dll”);
os << Format(hr, oleDbModule) << endl;
Input: Eksempelstring str;cout << "Input a string : ";cin >> str;
int I = 0;cout << "Input a number : ";cin >> i;
double d = 0.0;cout << "Input a float : ";cin >> d;
os << "String = '" << str << "', number = " << i << ", double = " << d << std::endl;
// KjøringInput a string : helloInput a number : 234Input a float : 200String = 'hello', number = 234, double = 200
// Glemte å vente på turInput a string : hello 235 23002.2Input a number : Input a float : String = 'hello', number = 235, double = 23002.2
// Ting går feil:Input a string : hello thereInput a number : Input a float : String = 'hello', number = 0, double = 0
Input
• Å lese på cin flusher cout. Dette er fordi strømmene er ”tied”:– cin.tie(cout);
• Input er white-space separert, selv fra konsol– Whitespace er definert i forhold til locale.
• Dersom man skriver noe uventet, leses ikke verdien– Man kan bruke funksjonen basic_istream::good() for å
se om det gikk galt
Input streams 2string str;cout << "Input a string : ";cin >> str;
int i = 0;cout << "Input a number : ";cin >> i;if ( !cin.good() ) {
cout << "Invalid number!" << std::endl;}
double d = 0.0;cout << "Input a float : ";cin >> d;if ( !cin.good() ) {
cout << "Invalid number!" << std::endl;}
cout << "String = '" << str << "', number = " << i << ", double = " << d << std::endl;
// KjøringInput a string : hello thereInput a number : Invalid number!Input a float : Invalid number!String = 'hello', number = 0, double = 0
Input: andre funksjoner
• istream& getline(char *s, streamsize n, char delim)
• peek, ignore, putback• istream_iterator• Mer finkornete feilhånteringsfunksjonalitet
– fail (kunne ikke lese) vs. bad (buffer i en ”farlig tilstand”) vs. eof (slutt på input)
• Mer fleksibel whitespace håntering
Egne operatoreristream& operator>>(iostream& is, vector<int>& v) { // Leser en vector som ser slik ut: ”[0, 2, 5, ... ]” char delim; v.clear();
// skjekk åpningstegn is >> delim; if ( delim != '[' ) { // Dette er ikke en liste is.putback(delim); is.clear(ios::badbit|is.rdstate()); return is; }
if ( is.flags() & ios::skipws ) is >> ws; if ( is.peek() == ']' ) return is; // Tom liste
// les elementer til vi finner avslutning ']' while ( is.good() && delim != ']' ) { int current = 0; is >> current >> delim; v.push_back(current); }
return is;}
Locales
• Et locale er beskrivelse av settings for brukerens plassering i verden
• Et locale inneholder flere facets. En facet angir settings for ett aspekt med localet
• Følgende standard facet eksisterer– ctype (character egenskaper)– collate (sortering), codecvt (tegnsett konvertering)– Penger: moneypunct, money_get, money_put– Numre: numpunct, num_get, num_put– Dato: time_get, time_put– messages
Locales og strømmer
• Ios_base, som er superklasse til ostream og istream definerer funksjonene:– getloc() returner locale objektet til strømmen– imbue() setter nytt locale objekt for strømmen
• Locale-objektet brukes til å formattere tall, men kan også brukes i egendefinerte operator<<
Eksempel med struct tminline ostream& operator<<(ostream& os, struct tm& tm){
typedef time_put<char> ttimeput;
ttimeput const& timeput = _USE(os.getloc(), ttimeput);timeput.put(ttimeput::iter_type(os),os,&tm, 'x');return os;
}
inline istream& operator>>(istream& is, struct tm& tm){
typedef time_get<char> ttimeget;
ttimeget const& timeget = _USE(os.getloc(), ttimeget);timeget.get_date(ttimeput::iter_type(is.rdbuf()), timeput::iter_type(),
is, is, &tm);
if ( !is ) return is;
timeget.get_time(ttimeput::iter_type(is.rdbuf()), timeput::iter_type(), is, is, &tm);
return os;}
Streambuffer
• Oppgaver– Hånterer kilde/destinasjon for lesing å skriving– Bufring/minnehåntering– Unicode/ANSI konverteringer
• Kan overstyres etter brukers behov• Gjør ingen formattering av output• Ganske stor og forvirrende klasse, så jeg
vil kun gi en grunnleggende oversikt
Streambuffer interface
• Input– in_avail() – input: chars som kan leses før sync– snextc() – returnerer neste character– sbumpc() – hopper en character fram– sgetc() – returnere nåværende character– sgetn(E *s, streamsize n) – leser neste n– sputbackc(E c) – angrer lesing av c– sungetc() – angrer forrige lesning
• Output– pubsync() – flusher bufferet– sputc(E c) – skriver neste char– sputn(const E *s, streamsize n)
std::basic_streambuf
• Baseklassen til alle strømbuffere• Gir en implementasjon av alle funksjonen, både
for bufret og ubufret I/O• Har to ”lag” med ”template method” patternet
– xgetn, xget, xputn – kan overstyres, men fungerer bra for de fleste formål, de kaller:
– uflow, underflow, sync og overflow, som bør overstyres
Ubufferet I/O
• En ubufferet streambuf sender I/O direkte til sin eksterne kilde
• Input: Må overstyre uflow til å returnere neste character
• Output: Må overstyre overflow til å sende characters
Eksempel: tee_streambuf
• Sender output til alle registrerte buffere
• Er ubufret (dvs. mellomlagrer ikke data)
• Overrider kun følgende metode:– Overflow
• Jeg har ikke laget kode for å manipulere bufferene som det videresendes til
tee_streambuf source:
template<class char_type>class tee_streambuf : public std::basic_streambuf<char_type>{public: virtual int_type overflow(int_type c) {
for ( Buffers::iterator pBuffer=m_listeners.begin(); pBuffer!=m_listeners.end();
++pBuffer ) {
if ( pBuffers->sputc(c) == traits_type::eof() )return traits_type::eof();
}
return traits_type::not_eof(c);}
private:typedef std::vector< std::basic_streambuf<char_type> > Buffers;Buffers m_listeners;
};
Buffer-basert Input
• Input buffer– eback(), gptr(), og egptr() angir start, current og slutt,
respektive
– setg() kan brukes til å sette disse pekerene
– gbump() brukes til å inkrementere current
• Basic_streambuf::xsgetn hånterer input til det ikke er mer igjen i bufferet, kalles underflow.
• underflow må hente mer til bufferet, eller returnere eof.
Buffer-basert output
• Output buffer:– pbase(), pptr(), epptr() angir buffer
– setp() setter buffer
– pbump() inkrementere current
• basic_streambuf::xsputn hånterer output til det ikke er mer plass i bufferet, så kalles overflow.
• overflow må skaffe mer plass, eller returnere eof• I tillegg må sync sørge for at bufferet flushes
Streambuffer implementasjon
Get Area (input) Put Area (output)
Unbuffered uflow overflow
Buffered underflow overflowsync
Buffer eback,gptr,egptr,gbump
pbase,pptr,epptr,pbump
Eksempel 2: tdebugstreambuf
• Bufret output streambuffer
• Sender output til Win32 API’et OutputDebugStream
• Fungerer kun som output
• Binder cout til seg selv, slik at cout går til debug-strømmen
Kildekode: tdebugstreambuf
class tdebugstreambuf : public std::basic_streambuf<TCHAR>{
tdebugstream() { setp(m_buffer, m_buffer, m_buffer+nBufSize-1);cin.rdbuf(this);
}// This method is called when the buffer is filled up and must be flushedvirtual int_type overflow(int_type c) {
if (traits_type::eq_int_type(traits_type::eof(), c))return traits_type::not_eof(c);
synch();sputc(traits_type::to_char_type(c));return traits_type::not_eof(c);
}// This method is called when user requests flushing of the buffervirtual int sync() {
::OutputDebugString(m_buffer);setp(m_buffer, m_buffer, m_buffer+nBufSize-1);return 0;
}
TCHAR m_buffer[BUFFER_SIZE]; // The buffer};
Bruk av tdebufstreambuf
• Lag et bufferobjekt
• Lag en basic_ostream<TCHAR>
• Send output til strømmen
tdebugstreambuf debugstreambuf;basic_ostream<TCHAR> traceStream(&debugstreambuf);
traceStream << _T(”Hello world”) << std::endl;
Bonus: Videre muligheter
• Jeg bruker i DataLayerSrv og AccountingSrv noen hjelpeklasser
• Stream-manager– Velger mellom flere strømmer avh. av oppsett– Eks. TraceMgr[TraceProgress] << ”Det går
fremover”
Stack-objekt som følger fremgang
• TraceBlock trace(”FunctionName”);– Gjør trace[TraceProgress] << functionName << ”entered”
• trace.Progress(”Doing something”);– Gjør trace[TraceProgress] << functionName << ”Doing
something”
• trace[TraceWarn] << ”Something went wrong”;• trace.NormalExit();
– Gjør trace[TraceProgress] << ”Function exited”– Dersom det ikke gjøres, vil destructor gjøre
trace[TraceWarn] << ”Abnormal termination. Last progress was ” << lastProgress;