Newsgroup: it.comp.lang.c++
Titolo: marco e simboletti traditori ##...

Ola a tutti quanti !! :)
Beh inzomma... domani si torna sui banchi... finora non ho mai vinto
al superenalotto (finora :)... e non riesco ancora a far funzionare
un'idea che mi e' venuta per 'agevolare' un mio pragrammino in c++...
azzo... potrebbe mai andare peggio?? :P
Beh, cmq all'ultima cosa magari ora rimedio :)
--- code start ---
#include "stdio.h"
#define mcrJUMPTOFUNCT(id, param) \
   myfunct_##id(param)
void myfunct_5(const int param=0)
{
   printf("Surprise!! :) <%d>\n",param);
}
int main(int argc, char* argv[])
{
   const int jump_id = 5;
   mcrJUMPTOFUNCT(jump_id, 4);
   return(0);
}
--- code end ---
Allora quello qui sopra sarebbe un po' il fulcro (yeah) del
problema...
L'idea era quella di poter agganciarmi alla funzione 'myfunct_5' (e
omonimi 1,2,3,4,etc) con una piccola ma bella macro :) come questa:
#define mcrJUMPTOFUNCT(id, param) \
   myfunct_##id(param)
ma purtroppo... un me funge :(
Il mio compilatore (vc++6) rifiuta categoricamente quello spezzone di
codice ricercando invano, alla chiamata della macro, la funzione
'myfunct_jump_id'... ?!?!?
Inzomma, se al posto di quella costante uso un numero (cioe'
'direttamente') funge... ma purtroppo cosi' non avrebbe futuro con
cio' per cui mi servirebbe veramente...
Esiste un modo per ovviare al problema?? (azz, riassuntino: in parole
povere, si puo' attraverso una variabile col valore 5, richiamare la
mia funzione myfunct_5 con quella traditrice di macro??? :)))
Grazie mille...

Usa un array di puntatori a funzione:
typedef   void (*FuncPtr)(const int);
FuncPtr Array= new FuncPtr[5];
Array[0] = myfunct_1;
Array[1] = myfunct_2;
/* ... */
Array[4] = myfunct_5;
Per chiamare la funzione basta
Array[1](parametro);
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------

On Tue, 14 Sep 1999 18:35:56 GMT, 
npe...@linuxfreak.com
 (freak) wrote:
Il ## esegue semplicemente una sostituzione letterale, e
quindi ti ci trovi il nome della variabile invece del valore.
Potresti pensare di rivedere la cosa usando dei puntatori a funzione:
void myfunct(int funcnum, int val);
void func1(int x);
void func2(int x);
void func3(int x);
void func1(int val) { printf( "\nFUNZ1: val=%d", val); }
void func2(int val) { printf( "\nFUNZ2: val=%d", val); }
void func3(int val) { printf( "\nFUNZ3: val=%d", val); }
typedef void (*funcptr)(int);
funcptr funclist [] = { func1, func2, func3}; 
void myfunct( int funcnum, int val )
  {
  funclist[funcnum](val);
  }
int main(int argc, char *argv[])
  {
  for (int i = 0; i < sizeof(funclist)/ sizeof(funcptr); i++)
    {
    myfunct( i, i );
    }
  return 0; 
  }
Non l'ho compilato, ma il sugo e' quello.

 Grazie mille...
-- 
Hello.  Jim Rockford's machine, this is Larry Doheny's machine.  Will you
please have your master call my master at his convenience?  Thank you.
Thank you.  Thank you.  Thank you.  Thank you.  Thank you.
		-- "The Rockford Files"

 scomodare dei function objects.
Nel nostro campo spesso i topolini si trasformano in elefanti... 
Mi e' capitato spesso di scrivere degli stupidi programmini che, in poco tempo sono 
diventati sofisticatissimi e pieni di funzionalita' inizialmente non previste.
Per questo motivo credo che sia sempre conveniente mantenere una certa generalita'
ed estendibilita' anche quando si scrivono programmi semplici.
Ad esempio, ho preso ormai da un po' di tempo l'abitudine di separare, quando posso,
interfaccia ed implementazione utilizzando quasi sempre classi virtuali pure, e simili.
Anche se mi costa scrivere qualche linea di codice in piu' (e vabbe', anche un certo 
overhead,che comunque, di questi tempi, e' praticamente impercettibile), quadagno 
sicuramente in estendibilita' e riusabilita'.
*Fabio*
-- 
One of the advantages of being a captain is being able to ask for
advice without necessarily having to take it.
                -- Kirk, "Dagger of the Mind", stardate 2715.2

 ed estendibilita' anche quando si scrivono programmi semplici.
Sono perfettamente d'accordo. Io cerco di utilizzare tutti i migliori
criteri anche quando scrivo un "Hello world". :-)
EMG


Certamente quello che dici e' vero nella maggior parte dei casi, e concordo che
quando e' possibile e' meglio progettare tenendo in considerazione estendibilita'
etc., ma esiste tutta una serie di situazioni in cui agire cosi' puo' non essere una buona
ide; nelle applicazoini che richiedono realtime stretto spesso il costo del polimorfismo
e' inaccettabile. Per un caso semplice come l'esempio in questione, vedo piu'
appropriata una soluzione semplice.

scomodare dei function objects.
Insomma... i funzionoidi generalmente sono un sintomo di disegno povero
(in ottica OO). Mi spiego...
La presesenza di un oggetto privo di stato e` IMO un campanello di
allarme, poiche` tale oggetto sostanzialmente esegue delle operazioni su
altri oggetti, anzicche` lasciare che siano i secondi a espletare il 
compito.


appropriata una soluzione semplice.
L'uso di puntatori a funzione e` esattamente la stessa cosa che usare il
polimorfismo. In tal senso quando proposi l'uso di puntatori a funzione 
lo feci unicamente perche` l'esempio era basato su uno stile 
procedurale. In un approccio OO fornire una buona soluzione sulla base
di quanto specificato non era (IMO) fattibile.


scomodare dei function objects.
Benche` posti a questo livello del thread, le considerazioni che seguono
si riferiscono anche ad altri messaggi di replica a quanto quotato...
Il discorso di ammazzare il topo (un tempo era la zanzara) col cannone
secondo me non si regge in piedi. Tale affermazione nasconde (come 
evidenziato negli altri post) l'assunzione che una applicazione 
inizialmente semplice si mantenga tale nel tempo. Nella realta` invece
accade che una cosa semplice diventi sempre piu` complessa, anche
perche` un sistema viene realizzato gradualmente partendo da un nucleo
di funzionalita` necessarie e solo inizialmente sufficienti.
Si potrebbero fare poi altre considerazioni in merito, comunque nel caso
in esame le cose IMO stanno diversamente da quanto supposto da Lupo
Buono anche se per *altri motivi* mi spingono in un certo senso a
dargli ragione quando sostiene che gli oggetti funzione non sono la
soluzione migliore.
In particolare secondo me la presenza di oggetti funzione (funzionoidi)
intesi come oggetti privi di stato costituiti solamente (o 
sostanzialmente) da metodi e` generalmente (ma non necessariamente) un 
errore di design, in quanto eseguono operazioni su altri oggetti
quando invece questi "altri oggetti" dovrebbero fornire degli appositi
metodi. Detto questo, e` da vedere poi se l'errore e` in interfacce
mal progettate o in una errata individuazione delle entita` in gioco.
Inoltre considerato che il problema era posto in ottica procedurale,
mi pare piu` sensata (o se preferite omogenea) una soluzione
procedurale, anche se e` la scoperta dell'acqua calda in quanto
altro non e` che la realizzazione di un meccanismo di late bindig
(il C++ c'e` l'ha gia, tuttavia meglio un buon approccio procedurale
che un mal riuscito approccio OO)!


appropriata una soluzione semplice.
Quanto dici e` corretto in linea di principio, tuttavia:
1) Senza dei test sul campo sostenere che "il costo del polimorfismo e`
    inaccettabile" e` una posizione eccessiva se non errata...
    Anche perche` in realta` il polimorfismo potrebbe variare molto a
    seconda dell'architettura sottostante (HW) e delle capacita` di 
    ottimizzazione del compilatore.
2) Usare una tabella di puntatori a funzioni e` la stessa identica cosa
     che ricorrere al polimorfismo.


     che ricorrere al polimorfismo.
Ho gia' osservato in un'altra risposta che non e' vero

polimorfismo. 
Permettimi di dissentire: tirare in ballo il polimorfismo significa introdurre
un dereferenziamento in piu' ad ogni accesso agli oggetti, quindi la soluzione coi
puntatori a funzione e' piu' efficiente.

compito.
Be', hanno una loro utilita', e sono molto usati in STL

 -----------------------------------------------------------------
-- 
Any programming language is at its best before it is implemented and used.


Naturalmente dipende dal tipo di applicazioni in cui lavori.
Normalmente il costo di quel dereferenziamento in piu' e' trascurabile. 
Se diventa non trascurabile e' un segno che la tua applicazione dovresti 
scriverla in assembler ;-)
Ho fatto una provetta (su una macchina abbastanza veloce):
ho invocato un metodo virtuale per 10 milioni di volte,
ho eseguito lo stesso metodo come free function, e ho misurato la differenza
media in circa 140 millisecondi. 
Quindi su una singola invocazione la differenza dovrebbe essere
di circa 14 nanosecondi (sono quel paio di accessi in memoria in piu')
Se questi 14ns in piu' sono cosi' critici allora non puoi lavorare a un livello
di astrazione molto alto...
*Fabio*
-- 
I'm successful because I'm lucky.   The harder I work, the luckier I get.

scriverla in assembler ;-)
E' trascurabile fino a che non devi fare girare il programma su un processore
lento; a quel punto la differenza si nota e come. E poi tutto e' relativo: se la
mia applicazione deve girare con un tempo di ciclo di 10 ms, ogni operazione
superflua costa carissima; e prima di dover passare a riscrivere tutto in assembler,
con grande spreco di tempo, elimino le funzioni virtuali.

puntatori a funzione e' piu' efficiente.
E` esattamente la stessa cosa, poiche` la VTABLE altro non e` che un
array di puntatori a funzioni. L'indirizzo della VTABLE (VPTR) e` noto
al compilatore cosi`  come l'indirizzo di un array di puntatori a 
funzione... cosi` come e` noto l'indice del puntatore desiderato.
L'accesso alla VTABLE quindi non costa ne piu` ne meno di un
accesso ad un normale array di puntatori a funzione...
In effetti il codice prodotto per risolvere una chiamata a metodo
virtuale:
ObjPtr->Method();
si risolve in pratica in
VPTR[FuncIndex](this+offset, /*altri parametri */); 
//  this+offset e` calcolato a compile-time
Il valore di VPTR e` noto al compilatore, semplicemente perche` 
esso e` posto in un punto ben preciso all'interno della rappresentazione
fisica dell'oggetto, generalmente all'indirizzo this (che altro non e`
che il valore di ObjPtr). In tal modo la scrittura di cui sopra diviene:
this[FuncIndex](this+offset, /* altri parametri */);
A questo punto si capisce che offset e` il valore necessario a
scavalcare il VPTR in modo che il primo membro dell'oggetto
sia riferibile senza altre operazioni che la dereferenzazione,
per gli altri membri e` comunque necessario sommare un
ulteriore offset.
In pratica la soluzione "esplicita" (quella "alternativa" al 
polimorfismo) differisce solo per il fatto che separa i dati su cui
operare dall'array di puntatori a funzione... ovvero
this == VPTR == array_di_puntatori_a_funzione;
this+offset == indirizzo_dei_dati_su_cui_operare;
il puntatore ObjPtr che nella versione ad oggetti contiene
il valore this (e quindi this+offset) nella versione "esplicita"
conterrebbe l'equivalente del valore "this+offset"
e` cioe` inerente al problema.
Quindi non c'e` alcuna differenza tra i due metodi (salvo
una implementazione da cani del polimorfismo).
Ovviamente tutto questo (che puo` sembrare confuso ad una
prima lettura) non e` necessariamente cio` che accade, ma
dimostra che esiste almeno una implementazione del polimorfismo
tale che le due soluzioni siano equivalenti anche da un punto di vista
degli overhead.
Naturalmente l'accesso all'oggetto per dereferenzazione del 
"this+offset" non puo` essere considerato overhead poiche` ad una
normale funzione dovresti comunque passare i parametri su cui operare e
come saprai il passaggio di un puntatore a struttura si rivela
generalmente piu` efficiente del passaggio di una copia, soprattutto se
il risultato della funzione si traduce in una modifica dei parametri.
Se poi il tuo compilatore non fa le cose per bene, non e` certo perche`
non sia possibile altrimenti.


//  this+offset e` calcolato a compile-time
Piccola correzione, e`
VPTR[FuncIndex](this, /*altri parametri */); 
sommare offset non e` conveniente, complicherebbe
la risoluzione di chiamate a metodi virtuali all'interno
di metodi virtuali.
La cosa pero` non cambia praticamente nulla.


Be', hanno una loro utilita', e sono molto usati in STL
Infatti ho parlato di campanello d'allarme e non di errore e comunque
in una ottica object oriented (STL non e` OO, benche` la programmazione
generica sia un sottoinsieme della OOP).


E` esattamente la stessa cosa, poiche` la VTABLE altro non e` .....
Non e' per niente la stessa cosa, c'e' una indirezione in piu'. Ho riprodotto l'esperimento di Fabio
 Lopiano, e i tempi che misuro (con clock()) sono mediamente:
710 con l'uso del polimorfismo
550 con l'uso dei puntatori a funzione
L'ottimizzazione e' di oltre il 20%, che non chiamerei esattamente trascurabile.

che il valore di ObjPtr). 
Il linguaggio non specifica dove e' posizionato il  vptr all'interno degli oggetti.
Ogni compilatore e' libero di metterlo dove vuole.

Ogni compilatore e' libero di metterlo dove vuole.
Cio` comunque non vuol dire che sia messo a caso.


Cio` comunque non vuol dire che sia messo a caso.
Ovvio.


Scusa se non ho capito, ma ci vuole pazienza :).
Allora:
vptr è l'indirizzo della vtable, un array di puntatori a funzioni (giusto?);
vptr[index] quindi rappresenta l'indirizzo della funzione che voglio chiamare?
 *(vptr[index]),  il deindirizzamento di un puntatore a una funzione cosa rappresenta?
oppure potrebbe essere:
vptr[index] indirizzo della locazione in memoria dove è memorizzato l'indirizzo della funzione
che voglio chiamare.
 *(vptr[index]) l'indirizzo della funzione che voglio chiamare.
Qual'è delle due? E se è la seconda possibilità non sarebbe più efficente la prima? Perché la
prima non si può implementare? E se sono sbagliate entrame le ipotesi ti spiegeresti in
maniera più esplicità?
Ciao e Grazie.
    FBF

 *(vptr[index]),  il deindirizzamento di un puntatore a una funzione cosa rappresenta?
In effetti neanche io ne sono molto convinto, pero` devo dare atto che
c'e` effettivamente un overhead in piu`...
Spinto dalla curiosita` ho dunque iniziato a spulciare l'asm
prodotto dal codice che ho utilizzato per misurare
l'overhead...
Purtroppo non capisco un granche` di assembler, ma ad occhio non mi
pare di aver trovato indirezioni in piu` e come da mia ipotesi
e` vptr==*this (almeno nel borland C++ 5.02) cioe`
vptr e` memorizzato come prima cosa.
Ecco il codice del mio test (per console NT e forse 9x)
#include <iostream>
#include <windows.h>
#define STRICT
#define WIN32_LEAN_AND_MEAN
struct Data {
  int a1, a2, a3;
};
typedef void (*func)(Data*);
void f1(Data* p) {
  //p->a1 = p->a2 = p->a3 = 1;
  return;
};
void f2(Data* p) {
//  p->a1 = p->a2 = p->a3 = 2;
  return;
};
void f3(Data* p) {
  //p->a1 = p->a2 = p->a3 = 3;
  return;
};
void f4(Data* p) {
  //p->a1 = p->a2 = p->a3 = 4;
  return;
};
class Test {
  public:
    virtual void f1();
    virtual void f2();
    virtual void f3();
    virtual void f4();
  private:
    int a1, a2, a3;
};
// impedisce eventuali ottimizz.
// una assicurazione!
class Test2 : public Test {
  private:
    int z;
};
void Test::f1() {
  //a1 = a2 = a3 = 1;
  a1 = 1;
  return;
}
void Test::f2() {
  //a1 = a2 = a3 = 2;
  return;
}
void Test::f3() {
  //a1 = a2 = a3 = 3;
  return;
}
void Test::f4() {
  //a1 = a2 = a3 = 4;
  return;
}
void PrintTime(LPSYSTEMTIME Ptr1,
               LPSYSTEMTIME Ptr2) {
  cout << "inizio: " << Ptr1->wHour
       << ':' << Ptr1->wMinute
       << ':' << Ptr1->wSecond
       << ',' << Ptr1->wMilliseconds << endl;
  cout << "fine: " << Ptr2->wHour
       << ':' << Ptr2->wMinute
       << ':' << Ptr2->wSecond
       << ',' << Ptr2->wMilliseconds << endl;
}
int main() {
  Data* Ptr = new Data;
  func* a = new func[4];
  a[0] = f1;
  a[1] = f2;
  a[2] = f3;
  a[3] = f4;
  Test* Obj = new Test2;
  a[0](Ptr);
  a[1](Ptr);
  a[2](Ptr);
  a[3](Ptr);
  Obj->f1();
  Obj->f2();
  Obj->f3();
  Obj->f4();
  LPSYSTEMTIME SysTime1 = new SYSTEMTIME;
  LPSYSTEMTIME SysTime2 = new SYSTEMTIME;
  ::GetSystemTime(SysTime2);
  for(int i=0; i<10; ++i) {
    ::GetSystemTime(SysTime1);
    for(long j=0; j<10000000; ++j) {
      a[0](Ptr);
      a[1](Ptr);
      a[2](Ptr);
      a[3](Ptr);
    };
    ::GetSystemTime(SysTime2);
    cout << "array di puntatori a funzione:" << endl;
    PrintTime(SysTime1, SysTime2);
    ::GetSystemTime(SysTime1);
    for(long j=0; j<10000000; ++j) {
      Obj->f1();
      Obj->f2();
      Obj->f3();
      Obj->f4();
    }
    ::GetSystemTime(SysTime2);
    cout << "polimorfismo:" << endl;
    PrintTime(SysTime1, SysTime2);
  }
  char b;
  cin >> b;
}
Ed ecco dei frammenti di asm
// a[2](Ptr)
push  esi                       // mette. Ptr nello stack
call [ebx+0x08]            // chiama la funzione
pop ecx                        // pulisce lo stack?   
// Obj->f3()
push edi                     // mette this nello stack
mov eax ,[edi]
call [eax]
pop ecx
//codice per ::f3(Data*) e Test::f3() (sono identici ovviamente)
push ebp
mov ebp esp
pop ebp
ret
Il punto ovviamente e` tutto nella chiamata del metodo
che non pare essere ottimizzata a dovere (o meglio this non e`
disponibile nel registro corretto (mentre l'indirizzo
dell'array si trova gia` in un registro adatto allo scopo).
Se questa interpretazione fosse corretta, allora il problema
e` tutto nell'implementazione (codice prodotto dal compilatore)
e/o nelle caratteristiche della piattaforma hw (su un risc
forse la cosa cambierebbe parecchio).
C'e` qualcuno che di assembler x86 ne capisce e puo`
confermare o smentire la cosa?


In relazione al discorso circa l'overhead, ho qualche novita` che
pare confermare ulterioriormente quanto da me sostenuto, ovvero:
Se a e` un puntatore a funzione
(*a)()
e
a()
sono la stessa cosa (la dereferenzazione esplicita e` opzionale con i
puntatori a funzione)
...ancora
a[1]()
e
(*a[1])()
sono ancora la stessa cosa.
Quindi nessuna dereferenzazione in piu`... e` tutta questione
di qualita` del codice prodotto da un compilatore.
Vedi "The C++ programming language" B. Stroustrupt
nella seconda edizione e` il paragrafo 4.6.9 
10-ima riga a pag. 135.



Il vptr e' un puntatore alla vtable; quindi vptr[index] e' un puntatore a una funzione della vtable.
In realta' tra chiamare la funzione con (vptr[index])() e con *(vptr[index])() non c'e' nessuna
differenza.
Il discorso che facevo io voleva solo evidenziare la differenza che c'e' tra implementare una certa
cosa usando un semplice array di puntatori a funzione (in stile C) e il fare la stessa cosa usando
le funzioni virtuali. Il secondo caso e' piu' lento perche' l'accesso alla vtable puo' avvenire solo
passando prima per il puntatore all'oggetto C++, quindi al vptr, e quindi alla vtable.

di qualita` del codice prodotto da un compilatore.
Le dereferenziazioni che avvengono in tale processo sono due:
- per accedere all'oggetto devo dereferenziare il puntatore all'oggetto ( p->oggetto )
- per accedere alla funzione nella vtable devo dereferenziare il vptr (  vptr[index] )
Quindi, rivedendo quello che dicevi nei tuoi post, le tue affermazioni sono vere
dal punto di vista interno dell'oggetto. Dal punto di vista dell'applicazione, invece,
occorre accedere all'oggetto tramite un ulteriore puntatore, e peccato che questo
sia l'unico modo per usare il polimorfismo, per cui la discussione e' piuttosto
accademica.
Non credo ci sia nulla che un compilatore possa fare per evitare questo prezzo
senza dire addio al binding dinamico.
La soluzione che avevo proposto usando l'array di puntatori in stile C e' piu'
veloce perche' salta il primo dei due dereferenziamenti di cui sopra. 
Stavolta mi sembra di non avere detto cazzate. :)

 Vedi "The C++ programming language" B. Stroustrupt
Stroustrup. :-)
EMG

 passando prima per il puntatore all'oggetto C++, quindi al vptr, e quindi alla vtable.
Capito. Grazie.
FBF

confermare o smentire la cosa?
Io (nel limite delle mia capacità) ci ho provato.
Ciao, Giuseppe
---------------------------------------------------------------------------
_Guybrush su IRCNet canale #programmazione
ICQ: 42227764
---------------------------------------------------------------------------

In che senso, potresti spiegarmi?
Un processore risc e` solitamente piu` ricco di registri e cio`
puo` facilitare nella scelta.


 di abbagli!
Quindi siamo arrivati alla conclusione che gestendosi da soli
l'equivalente dei metodi virtuali si può ottenere codice più efficiente?
EMG

 l'equivalente dei metodi virtuali si può ottenere codice più efficiente?
Inserendo i puntatori ai metodi direttamente nell'oggetto,
invece di un puntatore alla VMT, le chiamate ai metodi
virtuali sono leggermente piu' veloci (di pochissimo),
pero' gli oggetti diventano piu' grossi e la creazione
degli oggetti diventa piu' lenta.
In caso di piu' chiamate a metodi dello stesso oggetto, il
puntatore alla VMT puo' essere gia' in un registro, e il
passaggio in piu' sparisce.
Se i compilatori usano la VMT un motivo ci sara'...
Questo pero' non vuol dire che la VMT sia sempre la cosa
migliore, in certi casi possono essere meglio altre
soluzioni.
	Mauro

puo` facilitare nella scelta.
Hai ragione, lavorare con una CPU Risc è un pelino più facile perchè si
hanno molti registri a disposizione e non c'è bisogno di scervellarsi per
trovare posto per i propri calcoli. Spero che la situazione attuale degli
x86 (solo 4 registri general purpose!!!) cambierà con la nuova Intel
Architecture a 64bit.


l'equivalente dei metodi virtuali si può ottenere codice più efficiente?
class Obj {
  public:
    void Metodo();
};
con il polimorfismo scriveremmo ObjPtr->Metodo()
che costa:
1 dereferenzazione per this al fine di trovare vptr
1 somma vptr+Index
1 altra dereferenzazione per la chiamata indiretta 
   (*(vptr+Index))(ObjPtr)
1 passaggio del parametro this (this == ObjPtr)
Osserviamo ora:
struct S;
typedef void (* func)(S*);
/* riflette l'interfaccia reale di un metodo che ritorna
    void e non  riceve parametri (salvo this in automatico) */
struct S {
   func* Vptr;
   /* ... */
};
se scrivessimo
S Data;
/* ... */
Data.Vptr[Index](&Data);
/*  1 somma per a+Index
     1 dereferenzazione per la chiamata indiretta (*(Vptr+index))(&Data)
     1 passaggio di "this" dato da &Data */
Saremmo delle "bestie" degne di licenziamento poiche` disponendo gia
delle informazioni di tipo necessarie, stiamo ricorrendo ad una
inutile chiamata indiretta. Questo non e` l'equivalente del 
polimorfismo (in cui il tipo effettivo dell'oggetto non e` noto), ma e`
tutta un'altra situazione (vedi [1]).
Il mio errore e` nato dal non aver capito la sottile (mica tanto)
differenza.
Il polimorfismo in un linguaggio procedurale sarebbe implementato
cosi`:
S* Data = new ...  (la new non crea necessariamente un oggetto S)
(Data->*(Vptr[index])))(Data)
/* la scrittura potrebbe non essere corretta, ma credo renda
    l'idea */
/* 1 dereferenzazione di Data per accedere a Vptr
    1 somma per Vptr+Index
    1 dereferenzazione per la chiamata indiretta
    1 passaggio del parametro "this"  dato dal valore di Data */
Ovvero esattamente lo stesso costo (come era ovvio aspettarsi).
Nota:
  Questo schema va bene per la sola ereditarieta` semplice.
  Classi base virtuali ed ereditarieta` multipla nascondono
  un ulteriore overhead intrinseco.
[1] In sostanza una situazione del genere potrebbe avere senso
      se volessimo implementare un polimorfismo legato non al
      tipo dell'oggetto, ma piu` in generale alla sua storia. Ad esempio
      in seguito a certi eventi la "vtable" viene aggiornata 
      *esplicitamente nel programma*  riassegnando alcuni puntatori.
      Questa forma di polimorfismo non e` fornita dal C++ (e da nessun
      linguaggio OO a me noto), ad occhio e croce pero` potrebbe
      essere emulato (con un membro di tipo puntatore a funzione).
Ovviamente mi aspetto di averla sparata grossa ancora una volta!
Comunque fatemi sapere la vostra opinione.


virtuali sono leggermente piu' veloci (di pochissimo)
Vedi il mio precedente post, secondo me cosi` facendo
non implementiamo lo stesso tipo di polimorfismo.


 wrote:
Comunque non e` detto che un risc sia meglio di un cisc...
Tutto questo pero` e` abbondantemente OT.


soluzioni.
Mi stupirei di trovare un compilatore che fa una cosa del genere;
Spostare la vtable all'interno degli oggetti e' osceno. In progetti
complessi il numero di oggetti puo' facilmente superare le
centinaia di unita', ed anche il numero funzioni virtuali di una classe
puo' essere elevato.
Con una implementazione del genere, tutti gli oggetti di una classe 
diventerebbero facilmente enormi.
La soluzione vptr+vtable e' semplicemente il migliore compromesso.

 Comunque fatemi sapere la vostra opinione.
Boh, francamente non ti seguo più. Io per scelta mi rifiuto di scendere
a questo livello di analisi. Se ho bisogno di prestazioni maggiori
faccio in un'altro modo (cambio algoritmo, cambio linguaggio, integro
con un altro linguaggio, utilizzo una adeguata libreria, etc.).
EMG

a questo livello di analisi.
Ho semplicemente analizzato le due situazioni per cercare di capire
il perche` del differente overhead e le conseguenze dei due
approcci. La mia conclusione e` stata che i due approcci non risolvono
lo stesso problema (altrimenti significherebbe che il C++ e`
inefficiente perche` implementa il polimorfismo con una tecnica
non ottimale dal punto di vista della velocita`).


virtuali sono leggermente piu' veloci (di pochissimo),
Leggendo di sfuggita questo post, non avevo afferrato bene
il discorso... pensavo che tu volessi dire "facendo le cose da se..."
In realta` come ha fatto notare Lupo Buono ci sarebbe un
notevole overhead in termini di occupazione di memoria.
Inoltre negli oggetti derivati ci sarebbe un problema di
frammentazione della vtable (pensa all'aggiunta nella
classe derivata di nuove funzioni virtuali), una complicazione
le cui conseguenze potrebbero non essere banali.
