Metaprogrammazione in C++

Pubblicità
??? Uno dei vantaggi dei template e' che vengono validati al tempo della compilazione. Infatti quello che fa il compilatore e' creare una funzione per ogni tipo di variabile per cui lo abbiamo utilizzato, usando method overloading. In pratica e' quello che faremmo (a mano) noi, evitando le possibilta' di sbagliare (che sono alte facendo taglia e cuci).
Il concetto dei Template e' proprio come uno dei tanti altri concetti tipico della programmazione. Da' al programmatore la possibilita' di usarlo. Sta al programmatore decidere SE usarlo, a seconda del problema e del contesto. Come in TUTTI gli altri casi. Prendi la ricorsione per esempio; e' stupenda per una limitata classe di problemi, e' orribile per altri. Lo stesso con i Template.
Prova a scrivere un Windows server usando COM. Hai due possibilita': MFC e ATL, entrambe funzionano, ma un server scritto usando ATL e' MILLE volte meglio. Il codice e' piu' piccolo e quindi piu' facile da mantenere, l'eseguibile ha dimensioni minuscole, va piu' veloce, e' piu facile da farne il debugging.
Non per nulla il concetto di Template e' stato esportato anche su C# (linguaggio molto strongly typed) con l'introduzione dei "generic"
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-methods
Forse ho sbagliato con il termine “validati”. Intendo eseguiti.
Il compilatore genera ciascuna una funzione per ciascun valore possibile in template. In questi casi mi sembra molto ridondante avere una funzione o una struttura per tutti i valori da 1 a N.

Comunque la mia domanda è sempre la stessa: in contesti non generici come la struttura Fattoriale scritta da me sopra, ma anche una altra come Fibonacci (che ha una maggiore profondità in ambito ricorsivo) perché dovrei preferire la struttura ad una funzione constexpr ricorsiva (ma anche iterativa per il fattoriale)? C'è qualcosa di nascosto? Alla fine una funzione constexpr è pur sempre chiamabile con valori non noti a compilazione ma un template no.
 
Il discorso del fattoriale, secondo me, è fine a sè stesso. I template ti sono utili in altri contesti. Prova, a titolo di esercizio, ad implementare uno Stack, utilizzando come struttura dati solo gli array. Considera che ora la stessa struttura la puoi utilizzare per interi, per valori in virgola mobile, o per delle stringhe oppure ancora per un qualsiasi tipo di oggetto.

Se il tuo codice opera quindi solo su interi, o solo su un determinato tipo di dato, allora mi sentirei di dirti che puoi farne a meno. Alla fine lo scopo dei template è quello di essere utilizzati in contesti generici.
Riporto dal sito ufficiale:

Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type.

La ricorsione meriterebbe un discorso a parte... ci sono problemi che è meglio approcciare con la ricorsione (anche per leggibilità, chiarezza del codice), ed altri che è meglio approcciare iterativamente.
 
Il discorso del fattoriale, secondo me, è fine a sè stesso. I template ti sono utili in altri contesti. Prova, a titolo di esercizio, ad implementare uno Stack, utilizzando come struttura dati solo gli array. Considera che ora la stessa struttura la puoi utilizzare per interi, per valori in virgola mobile, o per delle stringhe oppure ancora per un qualsiasi tipo di oggetto.

Se il tuo codice opera quindi solo su interi, o solo su un determinato tipo di dato, allora mi sentirei di dirti che puoi farne a meno. Alla fine lo scopo dei template è quello di essere utilizzati in contesti generici.
Riporto dal sito ufficiale:



La ricorsione meriterebbe un discorso a parte... ci sono problemi che è meglio approcciare con la ricorsione (anche per leggibilità, chiarezza del codice), ed altri che è meglio approcciare iterativamente.
Quindi alla fine la metaprogrammazione è la “””semplice””” programmazione con i template. Implementare strutture dati con i template non è assolutamente dispendioso:
C++:
template <class T>
class Array
{
private:
    T* ptr;
    size_t size;
public:
    ...
}
E in ogni caso utilizzando la STL, tra strutture dati, sorting e puntatori intelligenti fai gran uso dei template.

Il fatto è che mi capitava di leggere di persone che per “master” (cosa in realtà impossibile) C++ ha acquisito la metaprogrammazione. E quando la cercavo per ottenere informazioni (dato che non ne avevo mai sentito parlare, e tuttora non ho ben compreso cosa comporti) mi ritrovavo con questo genere di algoritmi, dove tutto diventa una struttura template!

Sicuramente come detto da @pabloski aver studiato Lisp aiuta a comprendere il significato, ma nel mio piccolo di giovane programmatore gli unici paragoni che posso fare sono con il C# (dove nemmeno lì ho compreso le Reflection, tanto per dire).
 
Forse ho sbagliato con il termine “validati”. Intendo eseguiti.
Il compilatore genera ciascuna una funzione per ciascun valore possibile in template. In questi casi mi sembra molto ridondante avere una funzione o una struttura per tutti i valori da 1 a N.

Comunque la mia domanda è sempre la stessa: in contesti non generici come la struttura Fattoriale scritta da me sopra, ma anche una altra come Fibonacci (che ha una maggiore profondità in ambito ricorsivo) perché dovrei preferire la struttura ad una funzione constexpr ricorsiva (ma anche iterativa per il fattoriale)? C'è qualcosa di nascosto? Alla fine una funzione constexpr è pur sempre chiamabile con valori non noti a compilazione ma un template no.
Scusa, ma mi pare che tu stia facendo confusione. Template e constexpr sono due cose completamente diverse.

constexpr significa una funzione che viene eseguita durante la compilazione, e il cui risultato e' assegnato a una costante, che rimane costante, e segnala errore se tu la volessi cambiare. Puoi scrivere la funzione usando Template, quindi puoi mischiare le cose, che pero' rimangono distinte. In altre parole, se scrivi il fattoriale come constexpr, lo puoi calcolare SOLO con valori costanti, se cerchi di usarlo con una variabile ti sputa in faccia (in quanto il compilatore NON CONOSCE il valore di quella costante. Non e' una tecnica di programmazione, ma solo di ottimizzazione.

Per il Template invece il compilatore "genera" il codice solo quando il template viene utilizzato. Per esempio, lo chiami con un 'int'? Non fara' altro che "riscrivere" il codice sostituendo al Template il tipo 'int'. Lo stesso se lo chiami con 'double'. E basta.
 
Quindi alla fine la metaprogrammazione è la “””semplice””” programmazione con i template ...
NO, assolutamente.

La metaprogrammazione e' una caratteristica intrinsica di un linguaggio che permette al linguaggio di modificare il proprio codice, e' presente in ben pochi linguaggi tipo il LISP che proprio per quel motivo e' uno preferito per scrivere algoritmi di intelligenza artificiale (in quanto permette di scrivere programmi che possono cambiarsi da soli). Incidentalmente e' "quasi" la stessa tecnica usata dal compilatore C++ usando i Template, quindi quando si usano i Template per fare metaprogrammazione, si chiama "Template metaprogramming". Ma metaprogramming si puo' fare anche senza Template, e si possono usare i Template anche senza fare metaprogrammazione (infatti direi che non sia certo quello lo scopo dei Template)

Mi vedo costretto a ripetermi. Usare Template permette SOLO di scrivere meno codice, e quindi di essere piu' efficienti e avere meno possibilita' di errore QUANDO si ha un insieme di istruzioni che e' lo stesso ma cambia SOLO il tipo delle variabili. Tutto qui. Niente di piu', niente di meno. In pratica e' grosso modo l'equivalente di scrivere una funzione, che esegue sempre lo stesso codice cambiando SOLO il valore di alcune variabili (che sono i parametri della funzione): quando scrivi una funzione quello che eviti e' di fare "taglia e cuci" dello stesso codice: lo chiudi all'interno di una funzione che chiami con variabili diverse.

Ti consiglio caldamente ti leggerti gli articoli che ho linkato in precedenza.
 
Scusa, ma mi pare che tu stia facendo confusione. Template e constexpr sono due cose completamente diverse.

constexpr significa una funzione che viene eseguita durante la compilazione, e il cui risultato e' assegnato a una costante, che rimane costante, e segnala errore se tu la volessi cambiare. Puoi scrivere la funzione usando Template, quindi puoi mischiare le cose, che pero' rimangono distinte. In altre parole, se scrivi il fattoriale come constexpr, lo puoi calcolare SOLO con valori costanti, se cerchi di usarlo con una variabile ti sputa in faccia (in quanto il compilatore NON CONOSCE il valore di quella costante. Non e' una tecnica di programmazione, ma solo di ottimizzazione.

Per il Template invece il compilatore "genera" il codice solo quando il template viene utilizzato. Per esempio, lo chiami con un 'int'? Non fara' altro che "riscrivere" il codice sostituendo al Template il tipo 'int'. Lo stesso se lo chiami con 'double'. E basta.
In realtà quando una funzione è constexpr significa innanzitutto che è una funzione inline. Poi se possibile la valuta in compilazione ma è utilizzabile pure con parametri sconosciuti in esecuzione. Un parametro template richiede invece un valore const e static o una funzione constexpr
NO, assolutamente.

La metaprogrammazione e' una caratteristica intrinsica di un linguaggio che permette al linguaggio di modificare il proprio codice, e' presente in ben pochi linguaggi tipo il LISP che proprio per quel motivo e' uno preferito per scrivere algoritmi di intelligenza artificiale (in quanto permette di scrivere programmi che possono cambiarsi da soli). Incidentalmente e' "quasi" la stessa tecnica usata dal compilatore C++ usando i Template, quindi quando si usano i Template per fare metaprogrammazione, si chiama "Template metaprogramming". Ma metaprogramming si puo' fare anche senza Template, e si possono usare i Template anche senza fare metaprogrammazione (infatti direi che non sia certo quello lo scopo dei Template)
Ecco è una definizione che cercavo. Ovviamente già dal titolo intendevo di rimanere sul C++, quindi intendevo già la metaprogrammazione con template.
Mi vedo costretto a ripetermi. Usare Template permette SOLO di scrivere meno codice, e quindi di essere piu' efficienti e avere meno possibilita' di errore QUANDO si ha un insieme di istruzioni che e' lo stesso ma cambia SOLO il tipo delle variabili. Tutto qui. Niente di piu', niente di meno. In pratica e' grosso modo l'equivalente di scrivere una funzione, che esegue sempre lo stesso codice cambiando SOLO il valore di alcune variabili (che sono i parametri della funzione): quando scrivi una funzione quello che eviti e' di fare "taglia e cuci" dello stesso codice: lo chiudi all'interno di una funzione che chiami con variabili diverse.
E io ripeto che l’aspetto generico lo ho capito.
Ma si stava parlando del perché certa gente scriva strutture con valori come parametro del template, invece che di funzioni normali, e perché li spaccino come metaprogrammazione. Stop.
 
... Ma si stava parlando del perché certa gente scriva strutture con valori come parametro del template, invece che di funzioni normali, e perché li spaccino come metaprogrammazione. Stop.
Se ti limiti all'esempio classico, ossia del calcolo del fattoriale, e' ovvio che ti fai quella domanda. E' solo un esempio. Calcolare il fattoriale usando Template NON e' metaprogrammazione, anche se USA tecniche di metaprogrammazione. La metaprogrammazione va MOLTO piu' nei dettagli, ma ovvio che per spiegarla debbano fare un esempio semplice. Un po' come nel caso di "Hello world", ossia il primo programma che si impara a scrivere in un linguaggio, ossia un programma che scrive "Hello world" nel terminale. Se ci si limta a quello e' ovvio che uno si chieda "ma quello lo so fare pure io, prendo una penna e scrivo hello world, che ci vuole?". Che e' vero. Ma tutti noi sappiamo bene che programmare NON significa scrivere hello world. Ma figurati cosa succederebbe se avere una idea di come scrivere un proghrammino in un liguaggio ti chiedano di scrivere una rete neurale a un numero infinito di nodi... :)
 
Se ti limiti all'esempio classico, ossia del calcolo del fattoriale, e' ovvio che ti fai quella domanda. E' solo un esempio. Calcolare il fattoriale usando Template NON e' metaprogrammazione, anche se USA tecniche di metaprogrammazione. La metaprogrammazione va MOLTO piu' nei dettagli, ma ovvio che per spiegarla debbano fare un esempio semplice. Un po' come nel caso di "Hello world", ossia il primo programma che si impara a scrivere in un linguaggio, ossia un programma che scrive "Hello world" nel terminale. Se ci si limta a quello e' ovvio che uno si chieda "ma quello lo so fare pure io, prendo una penna e scrivo hello world, che ci vuole?". Che e' vero. Ma tutti noi sappiamo bene che programmare NON significa scrivere hello world. Ma figurati cosa succederebbe se avere una idea di come scrivere un proghrammino in un liguaggio ti chiedano di scrivere una rete neurale a un numero infinito di nodi... :)
Forse il problema è che non riesco a immaginare situazioni (che possa io realizzare) in cui mi sarebbero utili.
Se non dispiace allargo anche il discorso con le Reflection, in particolare prendendo come esempio il C#. Ad esempio, quale sarebbe il senso di construtti tipo
Codice:
Type t = typeof(string);
ConstructorInfo[] constructors = t.GetConstructors();
Quando mai mi servirà quell’array di info sui costruttori? Quando mai mi servirà dynamic?
 
La reflection è particolarmente utile per conoscere i dettagli della classe, piuttosto che dell'istanza della stessa. Un esempio fra tanti può essere la lettura dei commenti contenenti metadati (o generalmente "tag"), utili per dei middleware, piuttosto che per degli strumenti di analisi statica del codice o dichiarazione di servizi... o quello che ti pare :D (es. dichiarazione di lettura di un file binario all'interno di un oggetto tramite dichiarazione di dove andare a leggere / scrivere l'informazione).
 
Forse il problema è che non riesco a immaginare situazioni (che possa io realizzare) in cui mi sarebbero utili.
Se non dispiace allargo anche il discorso con le Reflection, in particolare prendendo come esempio il C#. Ad esempio, quale sarebbe il senso di construtti tipo
Codice:
Type t = typeof(string);
ConstructorInfo[] constructors = t.GetConstructors();
Quando mai mi servirà quell’array di info sui costruttori? Quando mai mi servirà dynamic?
Scusa, ma le tecniche di programmazione sono "infinite," nel senso che difficilmente le useremo tutte, usiamo solo quelle che fanno comodo a noi. Naturalmente occorre conoscerne il più possibile perché solo in quel caso sapremo quella migliore da usare per risolvere un particolare problema.

Template e Generics sono molto comuni, le usiamo tutti perché sono comode e ci fanno risparmiare tempo. La metaprogrammazione è piuttosto di nicchia e viene usata solo in qualche campo tipo intelligenza artificiale (io usai Lisp all'uni, poi non più). Reflection è tutto un altro discorso, anche quella è comodissima ma sarebbe meglio non parlarne qui.
 
Pubblicità
Pubblicità
Indietro
Top