/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
jdjespers.. 500
kyllekylle 500
Bech_bb 500
scootergr.. 300
gibson 300
molokyle 287
10  strarup 270
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




Søg
Reklame
Statistik
Spørgsmål : 177491
Tips : 31966
Nyheder : 719565
Indlæg : 6408458
Brugere : 218886

Månedens bedste
Årets bedste
Sidste års bedste