static int ota_seuraava(istream &is, char *s, int max_koko, char erotin)
{
if ( max_koko <= 0 ) return 0;
s[0] = 0;
if ( !is ) return 1;
is.getline(s,max_koko,erotin);
poista_tyhjat(s);
return ( is == 0 );
}
Tätä käyttäen voimme tehdä saman funktion
kuormitettuja versioita muille tietotyypeille:
int ota_seuraava(istream &is, double &d, char erotin)
{
char s[100];
int err = ota_seuraava(is,s,sizeof(s),erotin);
d = jono_doubleksi(s,"%lf");
return err;
}
int ota_seuraava(istream &is, int &i, char erotin)
{
char s[100];
int err = ota_seuraava(is,s,sizeof(s),erotin);
i = jono_intiksi(s,"%d");
return err;
}
Ja vielä tarvittaisiin lisää! Kuitenkin nämä
näyttävät keskenään yllättävän
samanlaisilta. Kirjoittamalla pari apualiohjelmaa ja vaihtamalla nimiä
saadaankin niistä täsmälleen samanlaisia:
int ota_seuraava(istream &is, double &a, char erotin)
{
char s[100];
int err = ota_seuraava(is,s,sizeof(s),erotin);
muunna_jono(s,a);
return err;
}
int ota_seuraava(istream &is, int &a, char erotin)
{ ... ihan sama ... }
inline void muunna_jono(const char *s,int &i)
{
i = jono_intiksi(s,"%d");
}
inline void muunna_jono(const char *s,double &d)
{
d = jono_doubleksi(s,"%lf");
}
Seuraavaksi herääkin kysymys: Kannattaako ota_seuraava-
funktiota kirjoittaa erikseen useita eri versioita yhden sanan muutoksella.
Kyllähän näitä leikkaa- liimaa - menetelmällä
syntyy kuin sieniä sateella, mutta entäpä ylläpito, jos
yleiseen kaavaan tuleekin pieni muutos. Kokemus osoittaa että jostakin
"samanlaisesta" funktiosta muutos unohtuu ja virhe paljastuu vasta aikojen
kuluttua!Onneksi C++:ssa on apu tätä varten: funktiomallit (function template). Idea on siinä, että jokin yleinen funktio kirjoitetaan tyyppiä vaille valmiiksi ja kääntäjä generoi sitten tästä mallista (muotista) tarvittavan määrän vastaavia todellisia funktioita niille tyypeille, joilla funktiota kutsutaan:
template <class TYPE>
int ota_seuraava(istream &is, TYPE &a, char erotin)
{
char s[100];
int err = ota_seuraava(is,s,sizeof(s),erotin);
muunna_jono(s,a);
return err;
}
Jos nyt on esimerkiksi kutsu:
int i; ota_seuraava(is,i,'|');generoituu tästä funktio jossa TYPE on korvattu tyypillä int:
int ota_seuraava(istream &is, int &a, char erotin);Nyt joudumme kirjoittamaan vaan muunna_jono- funktion kutakin eri tietotyyppiä varten. Tämä ei ole kohtuuton vaatimus, sillä kullekin tietotyypille olisi hyvä olla tapa muuntaa se merkkijonoksi (meillä jonoksi) ja päinvastoin.
Siis jos lisätään uusi tietotyyppi, kirjoitetaan funktiot:
void muunna_jono(const char *s,uusi_tyyppi &u); string jonoksi(const uusi_tyyppi &u);