Newsgroup: it.comp.lang.c++
Titolo: Metaprogrammazione template

Considerate il seguente template: in teoria dovrebbe servire per 
"trasformare" il tipo di un array cambiando il tipo base ma mantenendo 
invariate le dimensioni; ad esempio:
typedef Array_traits<int[10][20]>::Rebind<float>::Type float_array;
// il tipo di float_array dovrebbe essere float[10][20]
Il codice che ho scritto e` questo, ma non funziona :-(
// passo base del template. T e` un tipo non-array
template<class T>
struct Array_traits {
        template<class U>
        struct Rebind {
                typedef U Type;
        };
};
// passo induttivo: T e` un array
template<class T, int N>
struct Array_traits<T[N]> {
        template<class U>
        struct Rebind {
                typedef Array_traits<T>::Rebind<U>::Type Type[N];
        };
};
In teoria dovrebbe succedere qualcosa del genere:
Array_traits<int>::Rebind<float>::Type          ---> float
Array_traits<int[10]>::Rebind<float>::Type              ---> float[10]
Array_traits<int[10][20]>::Rebind<float>::Type  ---> float[10][20]
Putroppo non funziona: se provo a compilare trovo questo:
Array_traits<int>::Rebind<float>::Type          ---> float
Array_traits<int[10]>::Rebind<float>::Type              ---> float[10]
Array_traits<int[10][20]>::Rebind<float>::Type  ---> float[10] <- ERRORE
lo stesso errore si presenta anche con array a piu` dimensioni: in 
pratica viene considerata solo la prima e le successive sono 
"dimenticate".
Avete idea di dove sbagli? Piu` guardo il codice e piu` sono convinto 
che sia giusto :-(
ciao Anto
-- 
Antonio Cuni, 19 anni, studente
Brainbench C++ Master: 4.73, higher than 99% of all previous test takers.
C Language: 3.79, higher than 92% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 "dimenticate".
Ho riscontrato il medesimo comportamento con il mio compilatore (gcc), che
forse č lo stesso che usi tu. Per capire cosa effettivamente succede, ho
aggiunto due funzioni statiche, che ho chiamato trace():
in Array_traits<T>::Rebind<U>...
 static void trace()
 {
	cerr << __PRETTY_FUNCTION__<< endl;
	cerr << "standard: type=" << typeid(T).name() << endl;
  }
in Array_traits< T[ N ] >::Rebind<U>...   
 
 static void trace()
 {
	 cerr << __PRETTY_FUNCTION__<< endl;
	 cerr << "T[N] spec.: type=" << typeid(T).name() 
              <<" size=" << N << endl;
	 Array_traits<T>::Rebind<U>::trace();	// ricorsione...
 }
Sembra che il compilatore usi la specilizzazione per T[N] soltanto la
prima volta, e ricorra immediatamente alla forma standard, anche se T č
effettivamente di tipo array. Sono portato a pensare si tratti di un
errore del compilatore, ma non escludo che mi sfugga qualcosa.
Che ne pensi di questo, invece?
 template< class T, class U >
  struct array_rebind
  {
	typedef U type;
  };
 template< class T, int N, class U >
  struct array_rebind< T[N], U >
  {
	typedef typename array_rebind< T, U >::type type[N];
  };
//  array_rebind<int[10][20][30],float>::type -> float[10][20][30]
Questo funziona bene. Con gcc, almeno.
Max

 Che ne pensi di questo, invece?
[cut]
ottima idea! Per lo meno adesso abbiamo una versione funzionante che fa 
il suo dovere.
Mi piacerebbe pero` sapere il parere di qualcun'altro sulla questione 
originale: come mai gcc non specializza a dovere nel caso di un template 
annidato? Come si comportano gli altri compilatori?


annidato? Come si comportano gli altri compilatori?
So che suonera' un po' blasfemo... ma a nessun altro sembra
che questo modo di programmare "stoni" un po' con il resto
del C++ ?
Ho appena finito di leggere Modern C++ Design di Alexandrescu
e, pur essendo un forte sostenitore dei generatori di codice
(quando il codice generato e' comprensibile per il programmatore,
non quando si usano wizard che usano incantesimi incomprensibili)
e pur aprezzando quel testo per cose come il concetto generale
di policy trovo che questo voler scavare le fondamenta usando
i cucchiaini da caffe' sia quantomeno discutibile.
Con i template spesso ci sono cose che non si possono fare, ma
con qualche trucco quasi si riescono ad ottenere; ad esempio
non esiste una vera introspezione sui tipi... ma con paciocchi
di overload qualcosa riesce a saltare fuori (anche se manca,
in modo essenziale, la cosa secondo me principale: enumerare i
membri di una classe o i parametri di una funzione).
Non si possono fare loop ... ma si puo' usare la ricorsione
(e si e' obbligati... anche dove la natura del problema era
squisitamente iterativa). Non ci sono strutture dati, ma anche
qui con giochetti di ricorsione si puo' cavar fuori qualcosa...
(vedi typelist). Ovviamente il codice che ne viene fuori ha una
portabilita' praticamente nulla e una leggibilita' (ovvero
manutenibilita') secondo me assolutamente ridicola...
Nessun altro ha l'impressione che si stia utilizzando
completamente a sproposito il meccanismo dei template ?
Andrea

 completamente a sproposito il meccanismo dei template ?
io sono dell'idea che ogni strumento ha i suoi pro e i suoi contro. Se 
c'e' chi crede che l'obfuscated c sia una forma di programmazione da 
utilizzare, per me faccia pure. 
Se il template metaprogramming risolve problemi irresolubili altrimenti, 
ben venga, se e' un atto masturbatorio fine a se stesso, lo lascio agli 
esperti onanisti.
Tanto piu' che spesso integrando diversi linguaggi (ad esempio c++ e 
python, per non dire di eiffel o smalltalk) si compensano le rispettive 
carenze e si rinforzano i rispettivi pregi.


Il Capitolo 10 si apre con la seguente citazione :
I always knew C++ templates were the work of the Devil, and now I' m sure.
:-)
-- Cliff Click cited by Todd Veldhuizen
Battute a parte, non ho ancora avuto il tempo di approfondire (e quando mai
l' avrò ?) ma concordo sul fatto che non se ne sente la necessità d' uso
quotidiana.
Devo comunque dire che ormai la maggioranza del codice che scrivo è codice
template o ne fa ampiamente uso. Mi vengono ormai  i brividi a pensare di
dover scrivere le stesse cose con un linguaggio senza tale funzionalità. La
tecnica delle politiche di Alexandrescu è un ottima pensata, l' ho adottata
e mi ha semplificato davvero molte cose. Quindi, pragmaticamente, ho pensato
che non facesse male un approfondimento anche di queste tecniche un poco
estreme ed ora il libro prende polvere sulla mia scrivania. Ma potrebbe
venire utile ..... :-)
----
Sergio Bianchi

 completamente a sproposito il meccanismo dei template ?
quel che e` certo i template non sono stati pensati per questi scopi e, 
a dire la verita`, alcuni pensieri (come quello del "debug a compile 
time") mi potrebbero fare pensare che bisogna stare attenti a non tirare 
troppo la corda.
Secondo me tutto nasce dal fatto che i template sono piu` flessibili di 
quello strettamente necessario per cui li hanno progettati, ma forse non 
lo sono abbastanza per altri usi "creativi".
Senza dubbio la metaprogrammazione template presenta molti dark-corner, 
ma forse con opportune estensioni a livello di linguaggio (ad esempio 
una maggiore introspezione a compile-time, come hai tu stesso suggerito) 
si potrebbero raggiungere risultati incredibili: vedremo cosa uscira` 
fuori dal prossimo standard...


fuori dal prossimo standard...
scusate se mi intrometto...ma...come dire c'è bisogno di una
revisione dello standard attuale? se si...sommariamente cosa
non va attualmente?
--
Lawrence Oluyede
rhy...@myself.com

 arrangiarsi).
Mi spiegheresti esattamente cosa intendi, perchè non è possibile
fare "debug a compile-time" e che metodi usi per arrangiarti?
grazie anticipato
--
Gerolamo

fuori dal prossimo standard...
Hehehe... dovrei aspettare un altro granellino per
vedere quali altri incredibili territori (con trucchi
su trucchi su trucchi) mi permette di esplorare ?
Spiacente... ma ho di meglio da fare.
Quando ho voglia di un impegno mentale fine a se
stesso gioco a scacchi o mi metto a scrivere
codice in aree che mi interessano e che non ho
mai avuto necessita' di affrontare per lavoro.
Non credo ci sia interesse per realmente estendere
le potenzialita' di generazione di codice nel
prossimo standard. Credo anzi che la variazioni ed
estensioni saranno minime (esclusa la libreria).
Provero' ad informarmi pero'...
Andrea

 affrontando il problema alla radice.
cosa intendi?


 fare "debug a compile-time" e che metodi usi per arrangiarti?
penso che un esempio possa essere di aiuto; immagina un template in 
grado di calcolare a compile-time il fattoriale di un numero:
template<int N>
struct Factorial {
  enum { res = N * Factorial<N-1>::res };
};
template<>
struct Factorial<0> {
  enum { res = 1 };
};
Durante la compilazione quel che succede e` una vera e propria 
"esecuzione" del codice del template: se scrivi:
        int n = Factorial<3>::res;
il compilatore inizia a istanziare ricorsivamente il template, ottenendo
        int n = 3 * Factorial<2>::res;
e cosi` via, sino ad arrivare a
        int n = 3 * 2 * 1 * 1;
(da notare che il tutto avviene a compile time, *non* a run-time: se vai 
a leggere il codice assembly prodotto troverai direttamente la costante 
6).
Il problema nasce quando il codice che scrivi diventa piu` complicato ed 
e` facile introdurre errori: supponi ad esempio di scrivere:
template<int N>
struct Factorial {
  enum { res = N * Factorial<N-1>::res };
};
template<>
struct Factorial<0> {
  enum { res = 0 };
};
...
int n = Factorial<3>::res;
cout << n; // stampa 0 !!! :-(
In questo caso e` facile accorgersi dell'errore, ma supponi di non 
riuscire a trovarlo: purtroppo dato che il template viene "eseguito" a 
compile time non e` possibile seguire una esecuzione passo-passo (e` 
questo che intendo con "debug a compile-time"). Pero`, dicevo, ci si 
puo` arrangiare, scrivendo opportune funzioni che stampano messaggi di 
debug:
template<int N>
struct Factorial {
  enum { res = N * Factorial<N-1>::res };
  static void trace()
  {
        Factorial<N-1>::trace(); // "ricorsiva"
        cout << "Factorial<" << N << "> == " << res << endl;
  }
};
template<>
struct Factorial<0> {
  enum { res = 0 };
  static void trace()
  {
        cout << "Factorial<0> == " << res << endl;
  }
};
A questo punto puoi fare il debug in questo modo:
        Factorial<3>::trace();
il quale stampera` il seguente output
        Factorial<0> == 0
        Factorial<1> == 0
        Factorial<2> == 0
        Factorial<3> == 0
da questo output e` abbastanza facile accorgersi dell'errore e poterlo 
correggere.


 non va attualmente?
beh, ogni tanto salta fuori qualche difetto dello standard (ad esempio 
alcune parti in cui e` ambiguo o proprio sbagliato) e vengono emessi dei 
"defect-report" che hanno lo scopo di correggerlo.
In ogni caso, come per ogni standard anche quello del C++ verra` 
periodicamente aggiornato (un po' come e` successo al C con l'avvento 
del C99): sinceramente pero` non so dirti come sara` il prossimo 


 completamente a sproposito il meccanismo dei template ?
Completamente a sproposito certamente no. Dipende dalla concretezza
degli scopi che ci si prefigge. Certo che se si tratta solo di fare a
chi ce l'ha piu' lungo contando i minore e maggiore contenuti nel
codice...
Ti consiglierei di leggere "Generative Programming" di Czarnecki e
Eisenecker, ma penso che qua e la ti farebbe arrabbiare...
Ciao,
Nicola

 portabilita', in un campo minato.
Una tecnica che mi ha dato notevoli soddisfazioni e' generare con uno
strumento esterno specializzazioni di template. In questo modo riesco ad
ottenere il meglio dei due mondi e a combinarli insieme.
Ciao,
Nicola

 parser.
Hai scelto male i tuoi esempi ;-) Il solito Andrei mostra qui quali sono
le difficolta' nello scrivere una versione template di min/max:
http://www.cuj.com/experts/1904/alexandr.htm?topic=experts
Mentre Spirit e' un generatore di parser completamente metaprogrammato
(lo trovi qui: 
http://spirit.sourceforge.net/
)
Ciao,
Nicola

standard...
beh tu hai più esperienza di me...come la pensi in merito a
ciò? secondo te vi è qualcosa da modificare?
--
Lawrence Oluyede
rhy...@myself.com

 grado di calcolare a compile-time il fattoriale di un numero:
Chiarissimo, grazie!
--
Gerolamo

 ciò? secondo te vi è qualcosa da modificare?
Ho trovato un vecchio post di Manlio Perillo che parla proprio di 
questo: eccolo.
--- BEGIN FORWARDED MESSAGE ---
Ecco parte di un documento dello Stroustrup che ho prelevato dalla
sua homepage tempo fa e che credo possa risultare interessante.
                Directions for C++2x
- Extend standard primarily through major standard library additions
    - Provide support for distributed programming
    - Improve support for platform-independent systems programming
- Remove inconsistencies and errors from core language
    - Don't add major extensions
    - Remove embarrassments
- Offer a merger of C and C++ standards
    - We need a small joint C/C++ group to agree on rules for a merger
- Don't compromise C++ as a systems programming language
    - 0-overhead principle
- Minimize incompatibilities with C++98
    - Complete compatibility infeasible
    - Be as careful as the C committee was
                Standard library ideas
                (suggested concrete examples)
- Simple elements of standard platform
    - set of resource handles supporting 'resource acquisition is
initialization'
    - directories, TCP/IP, advanced I/O (async, multiplexed, memory 
mapped)...
- Distributed computing
    - XTI (eXtended Type Information)
    - Threads
    - Remote invocation (incl. Async)
    - Remote instantiation, name server interface
- Make the standard library central to bindings to other systems
    - CORBA, SQL, ...
- Add a few 'general utility' facilities
    - Hash_map
    - Pattern matching
                Core language ideas
                (suggested concrete examples)
- Increase consistency
    - identical lookup for functions and function objects
- Improve support for generic programming
    - template typedefs, typeof()
- Remove embarrassments
    - Frequent questions, frequent novice errors
        - a vector and a string that are range checked by default
        - Prohibit default copying of objects with destructors
        - Give a class with virtual functions a virtual destructor by 
default
    - vector<list<int>>
Saluti  Manlio Perillo
--- END FORWARDED MESSAGE ---


 ad ottenere il meglio dei due mondi e a combinarli insieme.
non ho capito: puoi fare un esempio?


 (lo trovi qui: 
http://spirit.sourceforge.net/
)
:-((
ciao Anto ;-)


Qui e' descritto lo stato delle librerie proposte per l'inclusione nello
standard. Vengono quasi tutte da boost:
http://www.esva.net/~beman/ftp/n13xx.html
Ciao,
Nicola

 non ho capito: puoi fare un esempio?
Ho scritto una libreria per la serializzazione di oggetti C++ da/verso
un DB relazionale. Per ogni coppia tabella di database - classe C++ ho
bisogno di una specializzazione del template
template <typename Tabella> PersistentClass { /* ... */ };
Il problema e' che il contenuto di ogni specializzazione dipende dai
membri dati della classe su cui si specializza, ovvero sara' qualcosa
del tipo:
class C {
  public:
    int i;
    double d;
};
template <> PersistentClass<C> {
  public:
    PersistentClass() : m_i("i"), m_d("d") {}
  private:
    PersistentAttribute<C,int> m_i;
    PersistentAttribute<C,double> m_d;
};
Ho scritto un programma che legge la struttura del DB, descritta in SQL,
e genera le mie specializzazioni; di fatto e' la porzione di libreria
che legge o scrive un istanza attributo per attributo. La parte di
libreria che manipola le istanze nel loro complesso e' invece realmente
generica.
E' piu' chiaro?
Ciao,
Nicola

    - template typedefs, typeof()
che significa?
--
Lawrence Oluyede
rhy...@myself.com



Se aggiungono il supporto per le espressioni regolari in C++ si
dovranno usare 4 "\" per ottenerne una? Non gioverebbe molto alla
leggibilità aggiungere al C++ qualcosa come le rawstring di Python?
-- 
Marco Barisione (ICQ 84277092)

>    - template typedefs, typeof()
typeof dovrebbe essere uno speciale operatore (risolto a compile-time) 
che "restituisce" il tipo dell'espressione passatagli come argomento 
(allo stesso modo in cui sizeof restituisce la dimensione 
dell'espressione passata come argomento).
Con template typedefs immagino che si intendano cose come:
template<class T>
typedef std::basic_string<T> mia_string;
cosa che attualmente e` (ovviamente) non permessa.


 E' piu' chiaro?
chiarissimo, come al solito.
In effetti il problema e` interessante: l'impressione e` che 
giochicchiando un po' con i template si possa riuscire ad ottenere 
qualcosa di semi-automatico (ad esempio sfruttando un meccanismo simile 
a quello usato da Alexandresco per GenScatterHierarchy) ma in questo 
caso sono d'accordo con Andrea: non varrebbe la pena complicare a 
dismisura il codice per ottenere risultati parziali quando facendo come 
hai fatto tu e` tutto piu` semplice.
Sarebbe invece interessante cercare di immaginare come sarebbe possibile 
automatizzare un lavoro del genere avendo a disposizione un qualche 
meccanismo di "riflessivita` a compile-time" che permettesse, ad 
esempio, di ottenere l'elenco dei membri di una data classe...


 esempio, di ottenere l'elenco dei membri di una data classe...
Questo e' il problema! Il punto e' che scrivere un parser per il C++ e'
un casino e quindi l'idea di generare codice a partire ad esempio dalle
dichiarazioni delle classi non e' molto allettante.
Pero' l'altra settimana sono stato all'ACCU Spring Conference dove, tra
tante cose interessanti, ho ascoltato Bjarne Stroustrup (proprio lui!)
che presentava il suo lavoro sulla libreria XTI (per extended type
information). Mi pare molto promettente.
Se il link e' ancora valido, qui trovi le slide della sua presentazione:
http://www.klid.dk/arrangementer/XTI_kbh.pdf
Ciao,
Nicola

cosa che attualmente e` (ovviamente) non permessa.
non conosco ancora i template....
--
Lawrence Oluyede
rhy...@myself.com

 built-in types.
Solo in apparenza, perche' essendo typeof valutata a compile time potra'
essere usata per dichiarare delle variabili.
Ad es.
vector<int> vi;
string s;
typeof(std::make_pair(s,vi)) element;
Al posto di
std::pair<std::string,std::vector<int> > element;
In questo esempio non conviene granche', ma con tipi piu' complessi gia'
di piu'. Ed e' di notevole aiuto nello scrivere template.
Ciao,
Nicola

 presentazione: 
http://www.klid.dk/arrangementer/XTI_kbh.pdf
ti ringrazio per il link, appena posso lo vado a visitare.
