Terminologia per template

Nel settimo capitolo di C++ Templates: The Complete Guide si parla della terminologia di base sui template.

Si é creata una certa confusione nella terminologia tecnica in questo campo. Ad esempio, ci spiegano gli autori, il termine template class viene utilizzato sia come sinonimo di class template, sia per riferirsi alle classi generate da template, sia per riferirsi a classi con un nome che é un template-id. Se la differenza tra la seconda e la terza non vi é chiara, beh, é buona cosa sapere che non siete da soli.

Altri termini che meritano una chiarificazione in questo contesto sono instanziazione e specializzazione, e dichiarazione nei confornti di definizione.

Proprietà del messaggio

Oltre ai campi standard definiti nell'header di un messaggio, é possibili definire dei campi custom, usando le funzionalità messe a disposizione da JMS per la gestione delle proprietà.

Il nome di una proprietà deve rispettare le regole definite per la sintassi di un selettore.
Una proprietà può assumere valori di tipo boolean, byte, short, int, long, float, double e String.

I valori di una proprietà per un messaggio sono fissati prima della sua spedizione. Per il cliente sono disponibili in sola lettura, il suo tentativo di modificarli risulta in una eccezione di tipo MessageNotWriteableException.

Per iterare la lettura delle proprietà di un messaggio si usi il metodo getPropertyNames() per ottenere un enumeratore dei nomi delle proprietà, e quindi si legga il valore associato per ognuno di essi.

E' possibile eliminare tutti i valori delle proprietà di un messaggio usando il metodo clearProperties().

Cercare di leggere il valore di una proprietà non definita per uno specifico messaggio fa ottenere null come risultato.

I campi dell'intestazione del messaggio

Gran parte dei campi dell'intestazione sono inizializzati dal metodo che lo manda:
  • JMSDestination
  • JMSDeliveryMode
  • JMSExpiration
  • JMSPriority
  • JMSMessageID
  • JMSTimestamp
Altri tre sono settati dal cliente:
  • JMSCorrelationID
  • JMSReplyTo
  • JMSType
E infine il provider setta
  • JMSRedelivered.
L'amministratore del sistema può configurare JMS per dare un valore specifico per JMSDeliveryMode, JMSExpiration e JMSPriority.
  • JMSDestination: destinazione a cui é stato mandato il messaggio.
  • JMSDeliveryMode: modo di spedizione secondo il quale é stato mandato il messaggio.
  • JMSMessageID: identifica univocamente ogni messaggio spedito da un provider
  • JMSTimestamp: é il momento in cui il messaggio é stato passato a un provider per essere spedito.
  • MSCorrelationID: può essere usato dal client per creare una relazione tra messaggi.
  • JMSReplyTo: la destinazione dove si dovrebbe mandare la risposta a un determinato messaggio.
  • JMSRedelivered: settato se il messaggio é stato mandato più volte, e quindi forse già recepito dal client.
  • JMSType: tipo del messaggio.
  • JMSExpiration: Settato a zero nel caso il messaggio sia sempre valido.
  • JMSPriority: priorità dal messaggio definita nell'intervallo [0..9], con 0 ad indicare il valore più basso.

Usare i template

Nel sesto capitolo di C++ Templates: The Complete Guide si fanno alcune utili considerazioni sull'uso pratico delle template.

Nel primo paragrafo si spiega come i dettagli implementativi propri dei template causino un dilemma e, possibilimente, anche degli errori a livello di linkaggio. La soluzione standard é quella del cosiddetto "inclusion model" che consiste in pratica nel mettere dichiarazione della classe template e la sua definizione nello stesso file che viene incluso dal codice che lo usa.

Nel secondo paragrafo si illustra il metodo dell'"instanziazione esplicita" che consiste in pratica nell'indicare esplicitamente quale template si voglia utilizzare, in modo da dare un suggerimento al linker sul lavoro da fare con la classe template. Il problema é che le regole sottese sono davvero poco elastiche: richiedere più di una volta l'esplicita istanziazione di un template porta a errori in fase di linkaggio e definizioni duplicate delle entità instanziate.

E' possibile sviluppare codice aperto alla scelta del modello inclusivo e della instaziazione esplicita due file di include per ogni template, includendo uno o l'altro header a seconda del modello che intendiamo usare. L'opzione viene dettagliata nel sottoparagrafo 6.2.2, a me sembra francamente sorgente di incomprensioni anche in un progetto di media dimensione, e quindi lascerei perdere.

Nel terzo paragrafo si mostra il separation model, in pratica si fa riferimento alla keyword export applicata alla dichiarazione di un template, in modo da renderla utilizzabile al codice seguente anche se la definizione del codice non é visibile. E' da notare che le funzioni inline all'interno di un template non vengono esportate, dato che non é possibile combinare export e inline.

Nonostante questo problema, il modello separativo sembra notevolmente più attraente di quello inclusivo, sfortunatamente ha altre limitazioni. Non molti compilatori supportano la parola chiave export, e quindi il codice scritto in questo modo rischia di non essere portabile. Secondariamente il modello separativo sposta il lavoro dalla fase di compilazione a quella del linkaggio, e nella realtà si scopre spesso che i vantaggi in termini di tempi impiegati per generare l'eseguibile non cambiano sostanzialmente. Inoltre ci possono essere dei problemi a livello semantico, che verranno trattati più avanti nel testo.

Il sesto paragrafo affronta il problema del debug per template. Verificare il funzionamento di un template per ogni possibile tipo sembra un compito impossibile, inoltre la diagnostica per gli errori generati all'interno di un template é a prima vista terrorizzante.

Complicazioni di base sui template

Il quinto capitolo di C++ Templates: The Complete Guide é dedicato ad aspetti dei template che pur essendo di base sono anche un po' spinosi.

Nel primo paragrafo si esplora l'uso di typename e del costrutto .template, situazioni che capiterà di rado di affrontare nella programmazione quotidiana, ma meglio non farsi cogliere impreparati.

Il secondo paragrafo mette sull'avviso nei confronti di una stranezza dei template. Nel caso di classi derivate l'uso di this-> é necessario per riferirsi a dati o metodi della superclasse. Vedremo meglio la faccenda più avanti, nel capitolo nove del libro, ci si assicura.

Il terzo paragrafo ci introduce alla parametrizzazione di membri di template.

Ad esempio, la nostra classe Stack potrebbe essere resa più elastica definendo l'operatore assegnamento in modo parametrico:

template <typename T>
class Stack {
// ...

// assign stack of elements of type T2
template <typename T2>
Stack<T>& operator= (Stack<T2> const&);
};

Ecco a seguire l'implementazione del metodo:

template <typename T>
template <typename T2>
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
{
if ((void*)this == (void*)&op2) {
return *this;
}

Stack<T2> tmp(op2); // create a copy of the assigned stack

elems.clear();
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}

In questo modo é possibile assegnare uno stack di interi a uno di float.

Nel quarto paragrafo ci si spiega come creare un template con parametri templatizzati. Utile se vogliamo creare, ad esempio, uno stack di cui specifichiamo sia il tipo dei dati trattati che il tipo del container utilizzato per memorizzarli, una cosa così:

Stack<int,std::vector<int> > vStack;

Notevole il quinto paragrafo, dove ci viene spiegato che, in un certo senso, é possibile chiamare una sorta di costruttore di default per i tipi di base. Ovvero, questo codice:

template <typename T>
void foo()
{
T x = T();
}

compila correttamente anche nel caso che T sia un tipo built-in, come ad esempio un int. Stupefacente, sensato, e molto utile. Il valore che viene assegnato é, molto propriamente, zero.

Il sesto paragrafo illustra un problema che, prima o poi capita a tutti: il fatto che l'uso di stringhe letterali risulta un po' ostico in connessione con i template.

Il problema é che una stringa come "apple" ha tipo char const[6], mentre "tomato" ha tipo char const[7] e non, come ci si potrebbe aspettare, lo stesso tipo.

Il paragrafo entra nei dettagli di questo problema fornendo alcune possibili soluzioni.