Newsgroup: it.comp.lang.c++
Titolo: Librerie e Template

Un saluto a tutti
Stavo provando a crearmi una classe che implementa un modello di Lista
puntantori del tipo
template<class tipodato> class Lista{...}
che poi sfrutterà nodi del tipo
template<class tipodato> class Nodo{...}
Il vero problema sta nel fatto che, finchè metto tutto all'interno di un
unico file sorgente, va tutto bene, mentre se provo a fare una libreria
esterna che contiene la classe, il compilatore crea la libreria compilando
tutte le funzioni della classe modello senza segnalare errori anche se
presenti (!) (forse perchè in realtà vengono compilate quando viene definito
un oggetto con il tipo nel programma finale ?), ed inoltre, quando il
programma che utilizza la libreria viene "Linkato" ad essa, non trova le
funzioni perchè naturalmente lui cerca qualcosa del tipo "Lista<float>
Funzione()" se il tipo scelto in fase di costruzione è float, mentre nella
libreria presumo ci siano prototipi del tipo "Lista<class tipodato>
Funzione()".
Devo forse aggiungere qualche direttiva durante la compilazione della
libreria ?
Ringrazio chiunque per la gentile attenzione
Saluti
Damianò

Un template e' per sua natura molto simile a una macro, solo che al
contrario di quest'ultima che viene semplicemente espansa dal preprocessore,
un template viene trattato e generato dal compilatore sul quale in piu'
esegue anche uno stretto controllo su sintassi e tipi.
Per creare una libreria di classi o funzioni template e' sufficente creare
uno o piu'  file header che contengono sia i prototipi che le
implementazioni.
Il programma che fa' uso di classi o funzioni template include questi header
e il compilatore crea il codice in base ai tipi dichiarati all'atto
dell'istanza (n copie del codice template adattato ai tipi passati
nell'istanza della classe o funzione).
Il modo in cui poi il compilatore crea l'istanza dell'oggetto (quante e
quali funzioni membro piu' o meno chiamate dal tuo programma) o della
funzione dipendono dalla qualita' del compilatore.
Se il tuo obbiettivo era quello di creare una libreria statica tradizionale
(tipo la c runtime library), contenente la tua classe template credo ti
sarai reso conto che non ha senso.
Se il tuo compilatore esegue il controllo sintattico del codice solo al
momento dell'istanza, e vuoi verificare o gestire la manutenzione del codice
della libreria in modo separato dai programmi, puoi creare una libreria
statica o un semplice eseguibile che contenga una serie di istanze 'zavorra'
di tutte le classi con i tipi predefiniti e provare e vedere se funzionano
oppure (se il tuo compilatore e' sufficentemente aggiornato) usare la
sintassi standard per forzare l'istanza di una classe es:
template class templatename<tipo>;
Saluti.


Le soluzioni che mi vengono in mente sono 2:
1) la libreria deve contenere solo metodi/funzioni inline, cioè in pratica
fornisce ai clienti oltre all'header delle classi anche il codice (template) di
tutti i metodi/funzioni.
Vantaggi:
i clienti possono crearsi tutte le istanze che vogliono delle classi/funzioni
template della libreria
Svantaggi
i clienti si ricompilano ognuno per conto suo le loro istanze delle
classi/funzioni e quindi il codice oggetto può risultare duplicato
2) la libreria espone alcuni "usi" delle classi/funzioni template. In pratica
nei file .cpp della libreria vanno inserite delle istruzioni come quelle sotto
che definiscono dei particolari usi dei template e che forzano la creazione del
relativo codice oggetto (forse questo si avvicina di più a quello che cerchi):
    template<int> class Lista;
    template<double> class Lista;
    template<MyClass> class Lista;
Vantaggi
. libreria precompilata, non duplicazione del codice
Svantaggi
. il cliente non può crearsi le sue istanze personali
Invito tutti a confermare le mie osservazioni, sono fresco di questi problemi
che sto affrontando proprio in questi giorni.
Ciao
    Alessandro Strazzari
    think3 R&D


Marco Savegnago wrote:

Un template e' per sua natura molto simile a una
macro, solo che al

contrario di quest'ultima che viene semplicemente espansa dal preprocessore,

un template viene trattato e generato dal compilatore sul quale in
piu'

esegue anche uno stretto controllo su sintassi e tipi.

Per creare una libreria di classi o funzioni template e' sufficente
creare

uno o piu'  file header che contengono sia i prototipi che le

implementazioni.

Il programma che fa' uso di classi o funzioni template include questi
header

e il compilatore crea il codice in base ai tipi dichiarati all'atto

dell'istanza (n copie del codice template adattato ai tipi passati

nell'istanza della classe o funzione).

Il modo in cui poi il compilatore crea l'istanza dell'oggetto (quante
e

quali funzioni membro piu' o meno chiamate dal tuo programma) o della

funzione dipendono dalla qualita' del compilatore.


In C++ Annotations ho trovato la seguente affermazione:

If the same template function definition was included in different
source files, which are then compiled to

different object files which are thereupon linked together, there will,

per type of template function, be only one


instantiation of the template function in the final program.


Questo mi fà molto piacere, ma vorrei sapere se questa imposizione
è parte dello standard o è a discrezione del compilatore?
In entrambi i casi ci si può fidare che compilatori come il MsVSC++5
seguano effetivamente questa indicazione?

Chiedo questo perchè ho visto progetti concreti dove si forzano
le istanze dei template o con la sintassi che riporti tu sotto o creando
dei puntatori "a perdere" (es: Array<Double> arrd(1);) non solo a scopo
di verificare le classi template, ma proprio, credo, per paura che venga
duplicato il codice per ogni istanza di una funzione template anche sullo
stesso tipo.

Sempre su C++ Annotations si trova anche la seguente discussione a
riguardo:

A third approach, is to declare template functions in header files,
keeping the definition in a template source file.

In the template source file the functions are instantiated by pointers
to the appropriate functions. For example,

define sumvector.cc as follows:

 

    template<class T>

    T sumVector(T *tp, unsigned n)

    {

        return (*tp);

    }

    static void

        *p1 = static_cast<int
(*)(int *, unsigned)>(sumVector);

 

and declare the sumVector template function in all sourcefiles using
sumVector. This way the compiler keeps

track of which sumVector() functions are required, linking them from
the sumvector.o object when

necessary. Of course, they must be available there. But if they aren't
then they can be defined simply by

providing another pointer defnition, followed by a recompilation of
sumvector.cc. The advantage here is gain in

compilation time (and maybe a clear overview of what template functions
are actually instantiated), as well as

data hiding: the implementation of the template function is not required
by the users of the implementation, and

can therefore be hidden from them. The disadvantage is the definition
of a bunch of static void * variables:

they are used as rvalues for the addresses of instantiated template
functions. Another disadvantage is that

the template definition is not available for other situations. If some
program would benefit from a sumVector()

instantiation for a type that is not available in sumvector.cc, the
template itself or the sumvector.cc

sourcefile would be required (since we strongly agree with the principles
of the free software foundation, the

latter disadvantage is actually more of an advantage in our opinion
:-).

Possibile che si preferisca ridurre i tempi di compilazione introducendo
la necessità di modificare i sorgenti di una libreria per ogni nuovo
tipo che i clienti vogliono gestire (mi sembra proprio brutto)?

Ad esempio STL come funziona?

Ciao

    Alessandro Strazzari

    think3 R&D







Grazie



    >In C++ Annotations ho trovato la seguente affermazione:
    >If the same template function definition was included in different
source files, which are then compiled to
    >different object files which are thereupon linked together, there will,
per type of template function, be only one
    >instantiation of the template function in the final program.
    >Questo mi fà molto piacere, ma vorrei sapere se questa imposizione è
parte dello standard o è a discrezione del >compilatore? In entrambi i casi
ci si può fidare che compilatori come il MsVSC++5 seguano effetivamente
questa >indicazione?
    Non conosco i dettagli dello standard a proposito di questo ma dalla mia
esperienza diretta, ho osservato che per quanto riguarda i function template
nei compilatori Microsoft  recenti (ad oggi posso verificare solo con la 6),
a istanze di funzioni template su moduli separati con uguale tipo e numero
di parametri corrisponde una sola copia di codice.
    Il caso tipico sono le funzioni min/max.
    >Chiedo questo perchè ho visto progetti concreti dove si forzano le
istanze dei template o con la sintassi che riporti >tu sotto o creando dei
puntatori "a perdere" (es: Array<Double> arrd(1);) non solo a scopo di
verificare le classi >template, ma proprio, credo, per paura che venga
duplicato il codice per ogni istanza di una funzione template >anche sullo
stesso tipo.
    Nel caso dei puntatori a perdere per classi template,  mi sembra di
ricordare che in passato si era costretti a fare una simile cosa per
forzare il linking del codice delle funzioni membro, perche' quando una
classe template veniva istanziata dinamicamente da una funzione contenuta
all'interno di una DLL il linker non era in grado di risolvere la chiamata.
(E' un ricordo molto vago e impreciso e non sono sicuro se questo accadesse
con i compilatori Borland)


    >        return (*tp);
    >    }



    La discussione che riporti puo' riferirsi a metodi di ottimizzazione da
eseguire 'a mano' quando il compilatore non supporta la fusione automatica
di template uguali. Se mi devo preoccupare di scrivere una libreria di
template in C++ raggruppare tutte le funzioni e usare i per poi usare i cast
ad ogni singola specializzazione tanto vale....
    STL e' scritta in C++ e penso che chi l'ha scritta non si sia
preoccupato di inserire tali ottimizzazioni (trucchi?), sopratutto perche'
credo sia un compito di chi progetta i compilatori.
    Saluti.

 
Supponiamo il seguente caso:
Lib: libreria template che esporta nei .hpp oltre ai prototipi anche
tutto il codice dell'implementazione
Dll1: include e usa Lib
Dll2: include e usa Lib
Application: main che si linka dinamicamente Dll1, Dll2
La mia impressione è che necessariamente Dll1 e Dll2 conteranno ognuna
una propria copia del codice binario per ogni istanza delle funzioni
template di Lib, anche per istanze della stessa funzione sugli stessi
tipi (quindi abbiamo lo stesso codice replicato). Come potrebbe il
compilatore ottimizzare un caso del genere, visto che Dll1 e Dll2 sono
generati ognuna per conto suo?
Ciao
	Alessandro

generati ognuna per conto suo?
Calma... le dll sono eseguibili separati a cui un altro eseguibili si collega.
Ovvero e` come se tu dicessi che per due eseguibili diversi hai codice
replicato.
Nel caso del tuo esempio le possibilita` sono 2:
a) le dll sono parte del tuo progetto, allora la colpa e` tua (in senso generico)...
    avresti dovuto utilizzare una terza dll per il codice comune e fare in modo che le
    due dll di partenza ne richiedano i servizi.
b) Provengono da terze parti e non sono sotto il tuo controllo. In questo caso
     chi ha prodotto le dll ricade nel punto precedente e tu semplicemente non
     puoi farci molto.
Un compilatore aderente allo standard (mi pare che tale comportamento
sia sancito esplicitamente dallo standard) deve garantire la cosa solo per il
singolo eseguibile. Per eseguibili diversi non si puo` pretendere lo stesso
comportamento.
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------


Il caso è quello (a). Ho una libreria di classi template (contenitori
generici, tipo STL, ma fatti in casa) usata largamente da tutti il
moduli del mio progetto (molti moduli, più  di 50) che attualmente sono
tutti linkati insieme staticamente.
L'obiettivo è di modularizzare meglio il tutto trasformando i vari
moduli in DLL o componenti COM.
In questo modo ricadiamo appunto nella situazione (a):
. una applicazione suddivisa in varie DLL
. molte DLL includono una libreria template
. la libreria template la posso implementare in due modi:
  . come un'insieme di header files che contengono i prototipi e le
implementazioni di  tutte le funzioni/metodi => ogni DLL se la include e
ricompila le proprie istanze => l'applicazione ottenuta dal link
dinamico di tutte le DLL contiene del codice duplicato che invece non
era nel caso del link statico
  . come una DLL che esporta un'insieme definito di istanze delle
classi/funzioni template => il codice delle istanze appartiene alla DLL
=> non viene duplicato il codice tra i vari moduli dell'applicazione.
Come svantaggio ho però l'introduzione di una dipendenza della libreria
verso i clienti, infatti se un cliente vuole una nuova istanza dei
template su un suo tipo interno, dovrò includerne il prototipo nella
libreria e ricompilarla per generarne l'istanza.
Quello che sto cercando di capire:
1) se le ipotesi che ho formulato sono corrette, specialmente riguardo
la valutazione dei vantaggi/svantaggi
2) qual'è la migliore ipotesi, sempre che ne esista una migliore
dell'altra
Ciao
	Alessandro


libreria e ricompilarla per generarne l'istanza.
Io opterei per questa soluzione (sempre che in presenza di multitreading
non si trasformi in un collo di bottiglia), la ricompilazione e` un problema
che puo` essere spesso bene ammortizzato da opportune tecniche (compreso
un accurato uso degli gli header precompilati, ma anche da un attento
uso di #include... inoltre generalmente si ricompila solo il necessario per
generare un certo eseguibili, non per tutti gli eseguibili del progetto).
