Newsgroup: it.comp.lang.c++
Titolo: Template con costanti e taglio parti di codice...

Spero di non alimentare una "fiammata" (tra l'altro molto interessante) 
come per l'altra domanda sulla posizione della dichiarazione delle 
variabili...comunque ecco la domanda.
Ho una funzione template di questo tipo:
template <int valore>
void funzione()
{
   switch (valore)
   {
     case 0:
       fai_una_cosa();
       break;
     case 1:
       fanne_unaltra();
       break;
   }
}
Allora, se la richiamo per esempio con...
funzione<0>();
...lo switch viene compilato così com'è o viene generata una cosa simile 
a...
void funzione()
{
   fai_una_cosa();
}
??
Mi interesserebbe saperlo sia per GCC che per Visual Studio. Purtroppo 
non ho tali conoscenze di assembler da poterlo verificare da me!
Grazie...

}
Non ho provato a controllare l'assembly, ma questo codice specifico mi
sembra un caso tipicamente ottimizzabile, saltando a pie' pari lo
switch. Il compilatore sa bene che non ha scelta, il valore è fisso.
Quindi eseguirebbe sempre e solo un ramo dell'albero delle scelte. Di
conseguenza può stracciare il ramo morto.

 non ho tali conoscenze di assembler da poterlo verificare da me!
Di per se non c'è scritto da nessuna parte che l'ottimizzazione deve
essere eseguita. Dal punto di vista della chiarezza del codice (ovvero
codice che fa *logicamente* quello che chiedi e che *necessariamente*
ottimizza come chiedi) io scriverei:
#include <iostream>
#include <ostream>
template <int val>
void f() {
    std::cout << "Foo" << std::endl;
}
template<>
void f<0>() {
    std::cout << "Boo" << std::endl;
}
int main ()
{
    f<1>();
    f<2>();
    f<0>();
    return 0;
}
Cioè con un briciolo di uso dei template risolvi a compile time il tutto
e amen.
-- 
-riko

 essere eseguita.
A mio avviso è ragionevolissimo che venga eseguita. Le ottimizzazioni
non sono obbligatorie.

 non sono obbligatorie.
E infatti tipicamente viene eseguita (controllato, btw).
Il punto è che lo stesso effetto lo posso ottenere in modo *certo*
usando i template. Non solo, ma IMHO è sicuramente più leggibile, visto
e considerato che la costante deve essere nota a compile-time usare la
specializzazione templatica.
Non ci sono ambiguità: quello voglio e quello lo scrivo in C++ a quel
modo. 
La versione dell'OP funziona? Si. L'ottimizzazione è ragionevole, ma
perchè usare un meccanismo a compile time contando sull'ottimizzazione
statica quando il linguaggio ti offre un meccanismo standard per
ottenere *esattamente* lo stesso risultato?
Oltretutto in modo molto più dichiarativo, IMHO.
-- 
-riko

 non ho tali conoscenze di assembler da poterlo verificare da me!
il compilatore microsoft ottimizza eliminando completamente lo switch. 
anzi, in casi simili emette anche un warning di condizione costante. a 
dire il vero è un'ottimizzazione cosi banale che mi stupirei se esistesse 
un compilatore che non ne tiene conto eh...

 ottenere *esattamente* lo stesso risultato?
... è un altro discorso. L'OP chiedeva se il codice viene ottimizzato
e secondo me quel codice viene SICURAMENTE ottimizzato. Non vedo buone
ragioni per non farlo. Il compilatore sa bene che comunque è sempre
c'è una sola strada da seguire. Tu dicevi che "di per se non c'è
scritto da nessuna parte che l'ottimizzazione deve essere eseguita".
Anche se è vero che nessuno obbliga il compilatore a ottimizzare, se
non ottimizza questo caso specifico, si tratta di un'implementazione
veramente molto scarsa (batto sempre sullo stesso punto).

 }
La seconda, almeno con GCC3.3.5 che ho sottomano(eh, lo so!), optimze 
default.
Vengono generate n funzioni  ognuna delle quali gestisce il solo codice 
indicato come
parametro. Con la massima ottimizzazione ... non funziona nulla (in 
debug :-() !
Non zo VS...
Bye
B.

> Non zo VS...
Mi rispondondo da solo ... con MsVS2005 , la funzione template
e' espansa cosi' come', come fosse una funzione e alla chiamata
viene pushato il valore sullo stack. Il prologo e l' epilogo della
funzione e' piu' grande dell' equivalente GCC.
bye

 veramente molto scarsa (batto sempre sullo stesso punto).
Ma assolutamente. Sono d'accordo. Anche io se dovessi scommettere direi
che probabilmente ogni implementazione lo fa. 
Il mio discorso era solo sul: già che ci siamo, facciamolo nel modo 'più
c++', che IMHO è usando la specializzazione dei templates.
-- 
-riko

>>>>>>>>>>>>>>>>>>>> NO OPT: DEFAULT/DEBUG
template <int valore>
void funzione()
{
004117C0  push        ebp
004117C1  mov         ebp,esp
004117C3  sub         esp,0C4h
004117C9  push        ebx
004117CA  push        esi
004117CB  push        edi
004117CC  lea         edi,[ebp-0C4h]
004117D2  mov         ecx,31h
004117D7  mov         eax,0CCCCCCCCh
004117DC  rep stos    dword ptr es:[edi]
   switch (valore)
004117DE  mov         dword ptr [ebp-0C4h],3
004117E8  cmp         dword ptr [ebp-0C4h],0
004117EF  je          funzione<3>+45h (411805h)
004117F1  cmp         dword ptr [ebp-0C4h],1
004117F8  je          funzione<3>+5Ah (41181Ah)
004117FA  cmp         dword ptr [ebp-0C4h],2
00411801  je          funzione<3>+6Fh (41182Fh)
00411803  jmp         funzione<3>+84h (411844h)
   {
     case 0:
       cout << "caso 0";
00411805  push        offset string "caso 0" (417820h)
0041180A  mov         eax,dword ptr [__imp_std::cout (41A2ECh)]
0041180F  push        eax
00411810  call        std::operator<<<std::char_traits<char> > (41115Eh)
00411815  add         esp,8
     break;
00411818  jmp         funzione<3>+97h (411857h)
     case 1:
       cout << "caso 1";
0041181A  push        offset string "caso 1" (417818h)
0041181F  mov         eax,dword ptr [__imp_std::cout (41A2ECh)]
00411824  push        eax
00411825  call        std::operator<<<std::char_traits<char> > (41115Eh)
0041182A  add         esp,8
       break;
0041182D  jmp         funzione<3>+97h (411857h)
     case 2:
       cout << "caso 2";
0041182F  push        offset string "caso 2" (417810h)
00411834  mov         eax,dword ptr [__imp_std::cout (41A2ECh)]
00411839  push        eax
0041183A  call        std::operator<<<std::char_traits<char> > (41115Eh)
0041183F  add         esp,8
       break;
00411842  jmp         funzione<3>+97h (411857h)
     default:
       cout << "caso default";
00411844  push        offset string "caso default" (417800h)
00411849  mov         eax,dword ptr [__imp_std::cout (41A2ECh)]
0041184E  push        eax
0041184F  call        std::operator<<<std::char_traits<char> > (41115Eh)
00411854  add         esp,8
       break;
   }
}
00411857  pop         edi
00411858  pop         esi
00411859  pop         ebx
0041185A  add         esp,0C4h
00411860  cmp         ebp,esp
00411862  call        @ILT+425(__RTC_CheckEsp) (4111AEh)
00411867  mov         esp,ebp
00411869  pop         ebp
0041186A  ret
 >>>>>>>>>>>>>>>>>>>>> MAX OPT: RELEASE
??$funzione@$02@@YAXXZ PROC				; funzione<3>, ; 12   : {
   00000	55		 push	 ebp
   00001	8b ec		 mov	 ebp, esp
   00003	83 e4 f8	 and	 esp, -8	;
   00006	a1 00 00 00 00	 mov	 eax, DWORD PTR 
__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
   0000b	68 00 00 00 00	 push	 OFFSET ??_C@_0N@JEDHGHIH@caso?5default?$AA@
   00010	50		 push	 eax
   00011	e8 00 00 00 00	 call	 
??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z 
; std::operator<<<std::char_traits<char> >
   00016	83 c4 08	 add	 esp, 8
   00019	8b e5		 mov	 esp, ebp
   0001b	5d		 pop	 ebp
   0001c	c3		 ret	 0
Tre funzioni come sopra.

 
D'accordo e grazie del suggerimento. Però l'esempio che ho fatto non era 
completo, nel senso che prima dello switch e dopo c'è molto altro 
codice, inoltre gli switch sono più di uno. Una cosa del genere:
template <int valore1, int valore2>
void funzione()
{
   ...codice...
   switch (valore1)
   {
     case 0:
       ...
       break;
     case 1:
       ...
       break;
   }
   ...codice...
   switch (valore2)
   {
     case 0:
       ...
       break;
     case 1:
       ...
       break;
   }
   ...altro codice...etc
}
Quindi l'unica (forse?) soluzione mi sembra essere quella di un'unica 
funzione template. Però devo essere certo di questa ottimizzazione in 
quanto gli switch sono all'interno di due cicli (x e y) che scandiscono 
un'interna bitmap di svariati milioni di pixel.
Sarebbe bello liberarmi degli switch, perchè così mi faciliterebbe 
l'implementazione di più casi valore1 e valore2, ma in realtà non so 
come fare.
Qualsiasi suggerimento è benvenuto!


se proprio devi ottimizzare al massimo, sarebbe bene evitare
codice che non sai cosa produce e dovresti semplificare
la vita al compilatore (tra l' altro: quale?).
Difficile anche dare suggerimenti: usa il massimo delle ottimizzazioni,
abilita le opzioni di fastcall (se ci sono)...
Ti serve un template perche' usi quel codice parecchie
volte? Oppure puoi crearti una funzione ad oc e richiamarla
quando ti serve? Ricordati che spesso uno switch viene
ottimizzato bene e cmp/jmp costano meno di una call con relativi
parametri( Quindi: se possibile evita la chiamata inutile di funzioni,
usa funzioni inline )
Forse caso potresti ricorrere alle buone vecchie tabelle di
funzioni: in questo caso l'indice punta alla funzione da
esguire e il compilatore non fa confonti ma chiama direttamente
la funzione in tabella (ovviamente il numero di funzioni e' NOTO
a priori, se no si fa un bel casino).
typedef void (*VOID_FUN_INT)(int);
void caso0(int sub)
{
     cout << "caso 0";
     switch(sub){..}
     // Oppure:
     SUB0_TAB[]=
}
void caso0(int sub)
{
     cout << "caso 0";
     switch(sub){..}
}
VOID_FUN_INT FUN_TAB[]=
{
   caso0,caso1
};
    ...........
    for(int j=0; j< 2; j++)
    {
     for(int n=0; n< 2; n++)
     {
       FUN_TAB[j](n);
       ....
    }
bye b.

 
Ehh, io non te ne posso dare, aspetto ancora di aver capito 
giusto e vedere se sia possibile (sul che ho dei dubbi)
ciao
Soviet_mario

 un'interna bitmap di svariati milioni di pixel.
Da come descrivi il problema sembrerebbe che valore1 e valore2 dipendano 
da x e y. In tal caso non puoi utilizzare le funzioni template.
E.

 E.

 Qualsiasi suggerimento è benvenuto!
Da quello che ho capito, *non* puoi usare da nessuna parte i template.
Le 'variabili' dei template devono essere note a compile time:
foo(int a, int b) {
  hello<a>(b);
}
*non* funziona.
Quello che invece *puoi* fare (ma è difficile, mica nulla da dire) è
*generare* dinamicamente il codice sulla base dei tuoi parametri (se
sono entrambi di indice finito e non troppo grosso puoi anche scriverti
proprio le funzioni e avere tutto il codice disponibile). 
C'era un articolo su Beautiful Code relativo all'uso del bytecode in C#
per ottenere ottime performance nel processing di immagini.
 
-- 
-riko

 possibile (anche se mi piacerebbe sbagliarmi).
No, non è possibile infatti. La templatizzazione viene interamente
risolta a compile time.
-- 
-riko

 templatiche a partire da valori VARIABILI determinabili a runtime ?
mi sono ancora spiegato male. Non sono variabili ma costanti. In realtà 
la funzione processa una pixmap. Questa pixmap può contenere differenti 
tipi di pixel (1 bit black/white, 8 bit con palette, RGB con 8 bit per 
ogni canale, CMYK, etc...).
Allora, mentre la funzione è generica per i tipi a canale (RGB, CMYK, 
CIELab), varia nel caso che i pixel siano compattati in byte (tipo 1 bit 
black/white) o indici di una palette.
E' per questo che ci sono gli "switch", solo per differenziare una 
piccola parte della funzione per quei casi particolari di pixel format.
Quindi la specializzazione diventerebbe qualcosa come:
funzione<tipoRGB>(immagine);
funzione<tipoBLACKWHITE>(immagine);
I tipi sono tanti, ma comunque limitati, ed è più importante la 
performance della lunghezza del codice.
Inoltre, gli switch sono proprio nei loop più interni, quindi sono molto 
costosi.

 performance della lunghezza del codice.
Ma allora perchè non usare proprio i tipi e scrivere codice ad oggetti
come dio comanda?
-- 
-riko

 impostazioni di default per ambiente debug
che non sono proprio le condizioni ottimali per effettuare test sulle 
ottimizzazioni eh :)

 ottimizzazioni eh :)
Ho riportato anche il codice 'ottimizzano' per par condicio.
Un compilatore furbo pero' quel codice non lo produrrebbe mai,
nemmeno senza ottimizzatore, tante' banale no ???
Mai fidarsi dei default di Microsoft ;-)
Bye
B.

brixrix99luftballons ha scritto:

Calma,
in debug io mi arrabbierei se il compilatore ottimizzasse!
In particolare, quello che mi aspetto io dal debug è che il codice 
prodotto segua per filo e per segno il sorgente del programma.
Anche se è un sorgente stupuido, inutile o quel che ti pare.
Anche perchè seno come potrei debuggarlo?
Ad esempio (ed è una cosa che ogni tanto faccio)
if(0)
{
  foo();
  bar();
}
e con il debugger io mi aspetto di poter andare (forzando) dentro all'if 
(cosa che in release non riesco a fare perchè quel codice non viene emesso)
GM

 GM
Certo, ma con i template QUEL codice non avrebbe dovuto essere prodotto 
cosi'!
infatti il gcc lo genera correttamente sia in debug che alla massima 
ottimizzazione.
Il discorso era pero' piu' generale di questo caso ..
Bye
B.

 ottimizzazione.
In quanto a correttezza, non credo che il VS in debug generi codice non 
corretto (e tra le due è più probabile che per via di qualche bug il 
codice non corretto sia quello ottimizzato).
GM