|
| fstream vs read/write-file Fra : PH Johansen |
Dato : 21-01-03 22:10 |
|
Nu har jeg programmeret c++ i et par år, og skriver stadig file io med c
funktionerne.
Jeg vil tro det er en almindelig situation, når man kommer fra c til c++, at
man er tilbøjelig til at lave c-kode med klasser istedet for "ægte" c++.
Jeg har efterhånden er kendt fordelene ved STL, men jeg kan faktisk stadig
ikke rigtig se fidusen ved at bruge de stream klasser som c++ kommer med...
det virker som om c funktionerne kan det samme og endda lidt nemmere.
Måske nogen her kunne tænke sig at smide nogen vægtige argumenter på bordet
for hvorfor jeg skal skifte til streams?
Det skal ikke forstås som at jeg er af den holdning at streams er
ligegyldige, og at jeg har bestemt mig for det og vil diskutere med dem der
mener modsat. Jeg går ud fra at disse klasser _er_ bedre, men jeg vil også
gerne høre hvorfor, da jeg ikke umidelbart kan se det.
| |
Mogens Hansen (21-01-2003)
| Kommentar Fra : Mogens Hansen |
Dato : 21-01-03 23:00 |
|
"PH Johansen" <ph@johansen.priv.net> wrote in message
news:b0kd03$1n4k$1@news.cybercity.dk...
[8<8<8<]
> Jeg vil tro det er en almindelig situation, når man kommer fra c til c++,
at
> man er tilbøjelig til at lave c-kode med klasser istedet for "ægte" c++.
Sådan oplevede jeg det også i lang tid.
[8<8<8<]
> Måske nogen her kunne tænke sig at smide nogen vægtige argumenter på
bordet
> for hvorfor jeg skal skifte til streams?
De 2 væsentligste egenskaber, der adskiller C++ streams fra C funktioner som
printf er:
1. typesikkerhed
2. udvidelighed med brugerdefinerede typer
Desuden er det simplere relation til allokering af buffere og sikrere i
relation til buffer overrun at bruge "std::ostringstream" i stedet for
"sprintf"
Ad 1.
I printf funktioner skal programmøren selv sikre at der er overensstemmelse
mellem format-strengen og typerne der faktisk sendes til funktionen. Nogle
compilere kan give warnings, hvis der ikke er overensstemmelse - men det er
ikke påkrævet.
Normal er det f.eks. simpelt at få printf til at fortolke det binære indhold
på stakken som en double, selvom der faktisk ligger en integer, med deraf
følgende underlige resultat.
I forbindelse med vedligehold, hvor man ændrer typen af en variabel, skal
man tilsvarende alle steder huske at ændre format-strengene.
I forbindelse med C++ stream bliver bestemmer compileren hvilken type der
skal skrives ud, og sikrer derved at det er den rigtige formateringsrutine
der kaldes.
Ad 2.
printf typen af funktioner kan kun udskrive de indbyggede typer, og der er
ikke nogen måde at udvide det på.
C++ streams kan udvides til at udskrive bruger definerede typer.
F.eks.
class person
{
public:
const string& name() const;
unsigned age() const;
// ...
};
// Hvis det skal være rigtigt pænt skal det være en
// template så forskellige tegn-typer understøttes
ostream operator<<(ostream& os, const person& p)
{
os << p.name() << ", " << p.age();
return os;
}
Så indbyggede typer og bruger definerede typer håndteres ens:
void foo(const person& p, float f)
{
cout << p << " " << f;
}
Det nyttige i at inbyggede og bruger definerede typer håndteres ens, ses
tydeligt i forbindelse med generisk programmering:
void foo(const vector<person>& persons, vector<float> floats)
{
copy(persons.begin(), persons.end(),
ostreamiterator<char, person>(cout, "\n"));
copy(floats.begin(), persones.end(),
ostreamiterator<char, float>(cout, "\n"));
}
(Ovenstående kode eksempler er ikke compileret - der kan være småfejl)
Venlig hilsen
Mogens Hansen
| |
Byrial Jensen (22-01-2003)
| Kommentar Fra : Byrial Jensen |
Dato : 22-01-03 20:56 |
|
Mogens Hansen <mogens_h@dk-online.dk> skrev:
>
> De 2 væsentligste egenskaber, der adskiller C++ streams fra C funktioner som
> printf er:
> 1. typesikkerhed
Ja!
> 2. udvidelighed med brugerdefinerede typer
> Ad 2.
> printf typen af funktioner kan kun udskrive de indbyggede typer, og der er
> ikke nogen måde at udvide det på.
Nej, ikke noget standardiseret metode, men i f.eks. GNU libc har man
mulighed for definere nye conversion specifiers for sine egne typer.
Det har jeg gode erfaringer med, men det er desværre ikke portabelt.
> Desuden er det simplere relation til allokering af buffere og sikrere i
> relation til buffer overrun at bruge "std::ostringstream" i stedet for
> "sprintf"
sprintf() er heldigvis blevet overflødig da snprintf() er blevet
standard i C99.
| |
Mogens Hansen (23-01-2003)
| Kommentar Fra : Mogens Hansen |
Dato : 23-01-03 07:15 |
|
"Byrial Jensen" <bjensen@nospam.dk> wrote in message
news:slrnb2tthu.1i9.bjensen@ask.ask...
> Mogens Hansen <mogens_h@dk-online.dk> skrev:
> > Desuden er det simplere relation til allokering af buffere og sikrere i
> > relation til buffer overrun at bruge "std::ostringstream" i stedet for
> > "sprintf"
>
> sprintf() er heldigvis blevet overflødig da snprintf() er blevet
> standard i C99.
ja.
snprintf løser den ene del af problemet: buffer-overrun.
snprintf løser dog ikke den anden del af problemet: bestemmelse af hvor stor
en buffer er der brug for
Venlig hilsen
Mogens Hansen
| |
Byrial Jensen (23-01-2003)
| Kommentar Fra : Byrial Jensen |
Dato : 23-01-03 20:02 |
|
Mogens Hansen <mogens_h@dk-online.dk> skrev:
> "Byrial Jensen" <bjensen@nospam.dk> wrote in message
>> Mogens Hansen <mogens_h@dk-online.dk> skrev:
>
>> > Desuden er det simplere relation til allokering af buffere og sikrere i
>> > relation til buffer overrun at bruge "std::ostringstream" i stedet for
>> > "sprintf"
>>
>> sprintf() er heldigvis blevet overflødig da snprintf() er blevet
>> standard i C99.
>
> ja.
>
> snprintf løser den ene del af problemet: buffer-overrun.
> snprintf løser dog ikke den anden del af problemet: bestemmelse af hvor stor
> en buffer er der brug for
Jo, det fremgår af retur-værdien. C99 siger:
7.19.6.5 The snprintf function
[#3] The snprintf function returns the number of characters
that would have been written had n been sufficiently large,
not counting the terminating null character, or a negative
value if an encoding error occurred. Thus, the null-
terminated output has been completely written if and only if
the returned value is nonnegative and less than n.
| |
PH Johansen (22-01-2003)
| Kommentar Fra : PH Johansen |
Dato : 22-01-03 21:55 |
|
> I forbindelse med C++ stream bliver bestemmer compileren hvilken type der
> skal skrives ud, og sikrer derved at det er den rigtige formateringsrutine
> der kaldes.
<snip andre gode grunde>
Det er gode argumenter for at bruge streams. Vil tage mig selv i nakken og
benytte dem nu... så godt jeg kan.
Hvordan gør man eksempelvis det svarende til fscanf med %*d for at indlæse
et heltal men ikke lagre det nogetsted. Altså at springe det over uden at
hælde det over i en dummy variabel.
Hvis jeg har følgende "10 20 30" og gerne vil læse 10 og 30 og springe 20
over...
med c routinerne kan man bruge formateringsstrengen "%d, %*d %d", var1, var2
og var1=10 var2=30
Kan ikke umidelbart se at det er muligt med streams
| |
Mogens Hansen (23-01-2003)
| Kommentar Fra : Mogens Hansen |
Dato : 23-01-03 07:14 |
|
"PH Johansen" <ph@johansen.priv.net> wrote in message
news:b0n0ei$28qq$1@news.cybercity.dk...
> Kan ikke umidelbart se at det er muligt med streams
Det er vist heller ikke muligt.
Man er nød til at inlæse tallet og derefter undlade at bruge det.
Hvis det er noget man tit har brug for, kan det simpelt laves:
<code>
#include <iostream>
template <typename T>
inline std::istream& skip(std::istream& is)
{
T t;
is >> t;
return is;
}
int main()
{
using namespace std;
int i, j;
cin >> i >> skip<int> >> j;
cout << i << ", " << j << endl;
}
</code>
Venlig hilsen
Mogens Hansen
| |
Jason Who (24-01-2003)
| Kommentar Fra : Jason Who |
Dato : 24-01-03 08:10 |
|
> <code>
> #include <iostream>
>
> template <typename T>
> inline std::istream& skip(std::istream& is)
> {
> T t;
> is >> t;
> return is;
> }
>
> int main()
> {
> using namespace std;
>
> int i, j;
> cin >> i >> skip<int> >> j;
> cout << i << ", " << j << endl;
> }
>
> </code>
Hvis jeg forsøger mig så får jeg compilerfejlen "dependent type qualifier
<int> is not a class or struct type".
Det både ved cut&paste af dine kode og skrivning af det selv. Det er med
borland builder. Måske fejlen siger dig elelr andre mere end den siger mig.
Tilsyneladende forekommer fejlen i _ios.h for basic_ios hvis den ønsker at
anvende int som en klasse?
| |
Mogens Hansen (24-01-2003)
| Kommentar Fra : Mogens Hansen |
Dato : 24-01-03 08:37 |
|
"Jason Who" <jw@nomail.here> wrote in message
news:b0qoqe$pdk$1@news.net.uni-c.dk...
[8<8<8<]
> Det både ved cut&paste af dine kode og skrivning af det selv. Det er med
> borland builder.
Jeg har også set den fejl-melding med Borland C++Builder V6.0 (og Borland
Kylix - men det er jo også samme compiler).
Jeg er temmelig sikker på at det er en fejl i compileren.
Koden virker med alle andre compilere jeg prøver:
* Comeau 4.3.0.1 (Hvilket er meget væsentligt)
* Intel C++ 5
* Microsoft Visual C++.NET
* gcc 3.2
men ikke med
* Borland C++Builder V6.0
* Borland Kylix C++ V3.0
* Microsoft Visual C++ V6.0
> Måske fejlen siger dig elelr andre mere end den siger mig.
> Tilsyneladende forekommer fejlen i _ios.h for basic_ios hvis den ønsker at
> anvende int som en klasse?
Compileren syntes, fuldstændigt ubegrundet, at den skal instantiere
"std::basic_istream<int, char_traits<char> >".
Prøv f.eks. i stedet:
<code>
#include <iostream>
template <typename T>
inline std::istream& skip(std::istream& is)
{
T t;
is >> t;
return is;
}
#if defined(__BORLANDC__)
inline std::istream& skip_int(std::istream& is)
{
return skip<int>(is);
}
#endif
int main()
{
using namespace std;
int i, j;
#if defined(__BORLANDC__)
cin >> i >> skip_int >> j;
#else
cin >> i >> skip<int> >> j;
#endif
cout << i << ", " << j << endl;
}
</code>
eller
<code>
#include <iostream>
template <typename T>
inline std::istream& skip(std::istream& is)
{
T t;
is >> t;
return is;
}
int main()
{
using namespace std;
int i, j;
#if defined(__BORLANDC__)
istream& (*skip_int)(istream&) = skip<int>;
cin >> i >> skip_int >> j;
#else
cin >> i >> skip<int> >> j;
#endif
cout << i << ", " << j << endl;
}
</code>
Koden bliver aldrig pænere af at skulle lave workarounds på compiler-fejl :(
Venlig hilsen
Mogens Hansen
| |
Jonas Meyer Rasmusse~ (23-01-2003)
| Kommentar Fra : Jonas Meyer Rasmusse~ |
Dato : 23-01-03 13:45 |
|
| |
Mogens Hansen (23-01-2003)
| Kommentar Fra : Mogens Hansen |
Dato : 23-01-03 16:22 |
|
"Jonas Meyer Rasmussen" <meyer@diku.dk> wrote
> Hej Mogens.
> Jeg forstår ikke helt hvordan det skal virke, din kode.
Ok.
Prøv at køre eksemplet.
Det burde umiddelbart være til at oversætte på en complaint compiler (jeg
har prøvet med Microsoft Visual C++.NET på MS-Windows og gcc 3.2 på Linux).
[8<8<8<]
> du kalder altså
> operator>>(istream&,skip<int>).
Ja - rund regnet (det var operator<<).
> Hvordan kan du det, når den ikke er defineret?
skip<int> er en pointer til en funktion, af typen
istream& (*)(istream&)
operator<<(istream&,skip<int>)
bliver alså opfattet som
operator<<(istream&, istream& (*func_ptr)(istream&))
eller rettere
istream: erator<<(istream& (*func_ptr)(istream&))
som heldigvis er defineret som et member-funktion i "std::basic_istream<Ch,
Tr>" (altså rundt regnet "std::istream").
> Vil det virke?
Ja.
> Umiddelbart ville jeg tro at du netop skulle definere
> din egen overload af operator>>(istream&,skip<T>)((og lave skip til en
> klasse)), sådan den virkede som en slags manipulator, men der er måske
> noget helt andet igang her?
Ja, det er netop en bruger-defineret manipulator jeg har lavet.
Kig f.eks. på beskrivelsen af "std::hex" for at se hvordan det virker.
Se f.eks.
The C++ Programming Language, Special Edition
Bjarne Stroustrup
ISBN 0-201-70073-5
side 631
Venlig hilsen
Mogens Hansen
| |
|
|