Newsgroup: it.comp.lang.c++
Titolo: compilazione classi template ed ereditarietà

Ciao a tutti,
ho avuto un problema durante il link di un mio programmino 
che utilizza template ed ereditarietà insieme. Premetto che non ho molta
esperienza di template e potrei avere scritto codice non corretto.
Penso di essere riuscito a risolverlo ma non ho capito perchè ora
funziona. Questo è quanto: inizialmente avevo 4 file:
file1.h
template <class T>
class i_base{
	virtual bool myfunc(void);
}
(so che questa classe ha poco senso ma ho tolto un po' di codice per
rendere l'esempio più semplice possibile)
file2.h
#include "file1.h"
template <class T>
class myclass:public i_base<T>{
	bool myfunct(void);
}
(qui praticamente definisco una classe che implementa le mia interfaccia
i_base)
file2.cpp
#include "file2.h"
template <class T>
void myclass<T>::myfunct(void){
...
}
file3.cpp
#include "file2.h"
int main(void){
	myclass<int> mc;
	return 0;
}
la compilazione va a buon fine:
g++ -c file2.cpp
g++ -c file3.cpp
però facendo il link mi dava sempre errore dicendomi che la funzione
myclass<int>.myfunct() era "undefined".
Ho provato ad unificate file2.h e file2.cpp, praticamento ho spostato le
definizione di myfunct nel file .h dove è definita la classe. cosi linka.
ma perchè?
non ho ancora controllato se fa effettevamente quello che mi aspetto ma
l'eseguibile almeno l'ho creato.
perchè se metto la definizione della funzione in un file separato non
riesco a linkare?
Grazie mille

 esperienza di template e potrei avere scritto codice non corretto.
<snip>
Le implemetazioni dei metodi template non possono stare in un file .cpp 
separato, devono essere conosciute per poter generare il codice relativo.
Io, in genere, utilizzo questo approccio:
->file.h
template <class T>
class i_base{
	virtual bool myfunc(void);
}
#include<file.hpp>
<-
------------
->file.hpp
template <class T>
void myclass<T>::myfunct(void){
...
}
<-
------------
->main.cpp
#include "file.h"


 riesco a linkare?
se conosci l'inglese leggi questo:
http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12
ciao!

 Io, in genere, utilizzo questo approccio:
[...]
Allora siamo in due.
Kiuhnm


Anch'io ho l'abitudine di tenere dichiarazione e implementazione di 
template in header distinti. Diversamente dall'esempio di logicMind, io 
però non #includo l'implementazione direttamente dall'header delle 
dichiarazioni, ma decido di volta in volta che cosa #includere. Questo mi 
consente maggior libertà nel decidere quanto codice compilare: spesso non 
serve che tutte le unità di compilazione #includino tutte le 
implementazioni, in quanto basta che esse siano definite una sola volta per 
ognuna delle istanziazioni richieste dall'applicazione.
Max

Max M. ha scritto:

Beh, non ha molto senso :-)
se ti serve la classe/funzione template ti serve anche 
l'implementazione, altrimenti non serve includere la dichiarazione.
E comunque il codice generato è solo quello utilizzato.
Ciao

 E comunque il codice generato è solo quello utilizzato.
Non è detto che usi tutte le funzioni di una classe, per es.
Kiuhnm

 ognuna delle istanziazioni richieste dall'applicazione.
IMHO, questo è peggio dell'ottimizzazione preventiva. Intervengo 
soltanto se i tempi di compilazione sono troppo alti.
Kiuhnm

 Kiuhnm
megaclasse troncata a secondo dei bisogni?
uhm, cattivo design?
P.S. Un linker ottimizzante tende a rimuovere codice inutilizzato
Ciao

 P.S. Un linker ottimizzante tende a rimuovere codice inutilizzato
No, il compilatore ignora il codice sorgente relativo.
---
template <class T>
struct A
{
	int f() { return 0; }
	int g() { return %&%&%&%&; }
};
int main()
{
	return A<int>().f();
}
---
Kiuhnm

 Kiuhnm
A maggior ragione non vedo la necessità di nascondere parti di codice.
Comunque mi riferivo a situazioni un po' più complesse, con ereditarietà 
dove magari si usano metodi della classe base template per aggiungere 
funzionalità, ma che poi non vengono utilizzate.
Quindi il compilatore, vedendo solo quel modulo, le implementa, ma il 
linker, avendo la visione globale, può rimuoverle.
Ciao

 A maggior ragione non vedo la necessità di nascondere parti di codice.
La ragione sono i tempi di compilazione.
Si può passare da giorni a qualche minuto.
Kiuhnm

 l'implementazione, altrimenti non serve includere la dichiarazione.
Ma non è vero. Per invocare una funzione ti basta la dichiarazione. 
L'implementazione può essere istanziata in una sola unità di compilazione 
(a meno, ovviamente, che tu non voglia averla 'inline'). 
Il vantaggio è che non devi ricompilare tante volte lo stesso codice e 
instanziare tante volte le stesse funzioni, e se cambi qualche dettaglio 
nelle implementazioni non devi ricompilare tutto di nuovo.
Max

 
Ehm, guarda che si parla di template.
Se poi lo specializzi, č un altro discorso.
Ciao

 Se poi lo specializzi, è un altro discorso.
Non capisco la risposta. Certo che parlo di template.
Io dicevo soltanto che se hai due sorgenti distinti in cui fai:
     A<int> a;
     a.function();
non c'è nessun motivo per cui l'implementazione generica di 
'A<T>::function()' debba essere #inclusa in entrambe le unità di 
compilazione. È sufficiente che venga #inclusa da una sola. Tutto qui. 
Per scrivere e compilare quel codice basta la definizione di A, non occorre 
avere visibilità sull'implementazione di 'function'. Quella serve solo per 
il linking. Per cui alla fine ci vorrà almeno una unità di complilazione 
che abbia istanziato 'A<int>::function'.
Che poi in generale sia più semplice #includere sempre tutto, beh, questo è 
un altro discorso.
Max

Il Sun, 20 Aug 2006 12:59:09 +0200, cosma ha scritto:
Ho trovato gli articoli molto interessanti, sono praticamente scritti per
risolvere il mio problema.
Grazie a tutti per i suggerimenti.



Non ti complichi la vita un po' troppo? Il tuo approccio e'
praticamente un'implementazione manuale di export... A questo punto non
sarebbe piu' pratico spostare le definizioni delle funzioni template in
un file .cpp ed istanziare esplicitamente quelle che ti servono?
Io in certi casi uso un approccio diverso. I template stanno in un file
.h strutturato cosi':
// Inclusioni richieste dalla definizione della classe
#include <vector>
#if defined(ISTANZIAZIONE)
//Inclusioni richieste dalle definizioni delle funzioni membro
#include <boost/shared_ptr.hpp>
#endif // defined(ISTANZIAZIONE)
template <typename T> class C {
  int f();
private:
  std::vector<T> v;
};
#if defined(ISTANZIAZIONE)
template <typename T> int C<T>::f() {
//...
}
#endif // defined(ISTANZIAZIONE)
Dopo di che una sola volta per specializzazione definisco ISTANZIAZIONE
prima di includere l'header e specializzo esplicitamente la classe:
#define ISTANZIAZIONE
#include <C.h>
template class C<int>;
Ciao,
Nicola

 
Alcune volte ho fatto cosě anch'io. Non mi sembra un approccio tanto    
diverso. L'unica differenza č che tu metti le implementazioni in un blocco 
#ifdef, mentre io le metto in un file a parte. Il vantaggio del mio 
"approccio" č che quando modifico qualcosa nelle implementazioni il sistema 
delle dipendenze non mi obbliga a ricompilare tutto.
Max

 delle dipendenze non mi obbliga a ricompilare tutto.
E' vero. Si scambia la necessita' di maggior accortezza con qualche
ricompilazione inutile. Alla fine credo che il raffinamento
dell'approccio in un senso o nell'altro dipenda sostanzialmente da come
i template vengono tipicamente usati.
Nel mio caso i template di cui parlo stanno sempre al di sotto di
un'interfaccia astratta, che comunque limita drasticamente i casi di
ricompilazione.
Ciao,
Nicola