Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Template loitsuja?

Sivun loppuun

Gaxx [29.09.2009 21:47:02]

#

Minulla on määritelty luokka:

class Buffer {
 public:
	//...

	template<typename T> bool write(const T& variable);
	template<typename T> bool read(T& variable);

	//...
};

Sitten käytän sitä tähän tapaan(toisessa tiedostossa):

Buffer buffer;
buffer.write(10); // undefined reference to `bool Buffer::write<int>(int const&)

Miten saisin kääntäjän luomaan tarvittavat versiot noista funktioista? Vai onko tämä sellainen asia, johon mallit eivät taivu ja joudun tekemään omat funktiot kaikille tietotyypeille?

Mainitaan vielä epäselvyyksien välttämiseksi, että homma toimii kyllä niillä tyypeillä, joita luokka käyttää sisäisesti.

Myös kaikista kummallisuuksista sopii huomauttaa :)

Metabolix [29.09.2009 22:44:13]

#

Ongelma johtuu siitä, että itse mallia ei käännöksen jälkeen ole, vaan on vain siitä luotuja funktioita.

Helpoin ratkaisu on tehdä noista inline-funktioita eli sisällyttää toteutuskin otsikkotiedostoon. Ohjelman koko ja käännösaika kasvavat hieman ainakin, jos funktio on iso, mutta toimiipa varmasti kaikilla tyypeillä.

Toinen vaihtoehto on erikseen määrätä tehtäväksi kaikki tarvittavat versiot. Tällöin pitää etukäteen tietää, mitä versioita muualla koodissa käytetään.

// C.hpp: esittely
struct C {
    template <typename T> void f(T t);
};

// C.cpp: toteutus ja mallin instantiaatio tyypeille int ja float.
template <typename T> void C::f(T t) {
    // ...
}

template void C::f<int>(int t);
template void C::f<float>(float t);

Tässä tapauksessa voi olla hyvä varmuuden vuoksi tehdä itse mallista yksityinen ja tarjota julkisesti käyttöön inline-wrapperit, jotka kutsuvat mallia. Näin vältytään ikäviltä yllätyksiltä.

struct C {
    void f(int t) { return _f(t); }
    void f(float t) { return _f(t); }
private:
    template <typename T> void _f(T t);
};

// Loput kuten äskenkin, tietysti funktiona _f eikä f.

goala [30.09.2009 11:42:24]

#

Luulisin, että tuo "undefined reference..." tulee siksi, koska templateja ei käännetä object-koodiksi (pois lukien specializationit). Näin ollen nuo täytyy olla "header only" -tyyppisiä, muutoin käy köpelösti.

Jos välttämättä haluat siirtää noiden implementationin erilliseen .cpp -tiedostoon, täytyy sun tehdä erillinen header safeguard myös .cpp tiedostolle ja lisätä se .h tiedoston loppuun includella.

Gaxx [02.10.2009 16:40:03]

#

Viimeinkin pääsin perehtymään asiaan ja vinkkienne myötä keksin siirtää mallien toteutukset otsikkotiedostoon. Nyt kääntäjä osaa luoda funktiot tarpeen mukaan.

class Buffer {
 public:
	//...

	template <typename T> bool Buffer::write(const T& variable) {
		// Toteutus...
	}

	template <typename T> bool Buffer::read(T& variable) {
		// Toteutus...
	}

	//...
};

Inline funktioiden kanssa tulisi ymmärtääkseni ongelmaksi viittausparametrit.

Kiitokset!

Metabolix [02.10.2009 16:42:51]

#

Nuohan nimenomaan ovat luokan inline-funktioita. Se, kirjoitatko ne noin vai luokan ulkopuolelle inline-sanan kanssa, on aivan makuasia.

struct C {
  void f() {
  }
};
struct C {
  void f();
};

inline void C::f() {
}

Gaxx [02.10.2009 18:05:45]

#

Jahas :) En ole aikaisemmin perehtynyt inline-funktioihin(vaikka olen niitä näemmä sitten käyttänyt) ja ymmärsin ne hieman väärin pikaisella googletuksella.
Nyt asian pitäisi olla selvä ja homma toimii.

Kiitokset oikaisusta :)

goala [05.10.2009 10:36:04]

#

Metabolix kirjoitti:

Nuohan nimenomaan ovat luokan inline-funktioita. Se, kirjoitatko ne noin vai luokan ulkopuolelle inline-sanan kanssa, on aivan makuasia.

struct C {
  void f() {
  }
};
struct C {
  void f();
};

inline void C::f() {
}

Parempi käytäntö on tosin kirjoittaa inline-funktiot luokan ulkopuolelle. Mikäli koodin kirjoittaja kirjoitaa inline-metodin suoraan luokan sisälle on se hankalampaa kolmannelle osapuolelle lukea sitä, sillä se sekoittaa keskenään asiat "mitä" ja "kuinka".

Kaiken kukkuraksi, olisi ehkä parasta tehdä asia näin:

class Foo
{
        public:
                void Bar();

        ...
};

inline void Foo::Bar()
{
        ....
}

Tähän perusteluksi se, että Sinun päätös siitä, onko metodi inline-tyyppinen vai ei on puhtaasi toteutuksellinen kysymys eikä muuta luokan ulkopuolisen havaitsian käsitystä funktiosta (sen tarkoitus).

Gaxx [05.10.2009 18:09:10]

#

Tämän hetkinen toteutukseni on seuraavanlainen:

// file.hpp

class Foo {
 public:
   template <typename T> void Bar(T& t);
};

template <typename T> void Bar::Bar(T& t) {
}

Eli toteutus on otsikkotiedostossa ja ilman inline-määrettä.

Homma toimii, kuten haluankin. Tällöinhän kyseessä ei ole inline-funktio?

Tietysti toteutuksen sijoittaminen otsikkotiedostoon on vähän kyseenalaista...

Metabolix [05.10.2009 18:29:56]

#

Jos homma toimii, anna homman olla. :)

Mainitaan nyt vielä, että ongelma ratkeaisi standardin mukaan export-sanalla, mutta mm. GCC ilmoittaa tylysti, ettei tue kyseistä ominaisuutta. Kunnollinen (tehokas) tuki tälle olisikin melkoinen temppu, kun tuotettuun ohjelmaan (tai esimerkiksi DLL-tiedostoon) pitäisi käytännössä sisällyttää oma kääntäjä.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta