Newsgroup: it.comp.lang.c++
Titolo: Uso del puntatore -> "al volo"

Il problema e' questo:
Uso un puntatore (i suoi metodi) senza sapere se il puntatore in
questione e' valido o nullo.
Es.
pippo.trova_oggetto(pluto)->inserisci_valore(10);
pippo.trova_oggetto(pluto) rende un puntatore che
puo' essere nullo o valido, a seconda se riesce o no
a trovare pluto.
A questo punto il inserisci_valore(10) potrebbe, o
meglio l'operatore ->, potrebbe "puntare" a un puntatore
nullo.
Posso creare un operatore (overload ->) che mi salva da
questa condizione ?
Altri metodi per aggirare l'ostacolo?
Non so se sono riuscito ad essere sufficentemente chiaro.
Ciao Fabrizio.

 questa condizione ?
Se pippo.trova_oggetto(pluto) restituisce un puntatore ad una tua
classe, puoi ridefinire operator -> per quella classe; ma cosa c'e' di
malvagio nello scrivere
if (pippo.trova_oggetto(pluto)) {
  pippo.trova_oggetto(pluto)->inserisci_valore(10);
}
magari seguito da un else std::cerr << "de'!  pluto non c'e'!\n"; ?
-- 
Maurizio Loreti                      
http://wwwcdf.pd.infn.it/~loreti/mlo.html
Un. of Padova, Dept. of Physics - Padova, Italy          
lor...@padova.infn.it


Hai ragione !
Pero' il 99% delle volte pluto c'e' e questi controlli vengono
fatti ciclicamante dentro un loop.
Pulizia formale del sorgente, metti 100 trova_oggetto sparsi con
relativo controllo e possibili dimenticanze.
Ti ringrazio e aspetto suggerimenti.
Ciao Fabrizio.

questa condizione ?
Il problema e` che trova_oggetto() puo` potenzialmente
tornare un puntatore nullo e quindi in tal caso bisogna evitare
di applicare operator->, ovvero trasformare questo passo e
i successivi in operazioni nulle.
A questo punto io sceglierei la soluzione
Obj* Tmp = pippo.trova_oggetto(pluto);
if (Tmp) Tmp->inserisci_valore(10);
oppure definirei una funzione
inline void VerificaEInserisci(Obj* obj, int value) {
   if (obj) Obj->inserisci_valore(value);
}
Comunque se proprio ci tieni a eseguire l'overloading
di operator->     (complicandoti la vita...) mi e` venuto in
mente un possibile approccio.
Quanto segue e` una idea che non ho il tempo di vedere se funziona.
L'idea e` la seguente: operator-> va applicato ad un puntatore non
nullo (e di tipo diverso da void) e deve restituire un puntatore di tipo
opportuno (nel tuo caso poniamo Obj).
Dato che operator -> deve in ogni caso restituire un puntatore valido [1],
e` necessario fare in modo che se il puntatore restituito da trova_oggetto()
e` nullo, operator-> restituisca un puntatore ad un oggetto dummy il cui unico
scopo e` "assorbire" la successiva dereferenziazione e renderne nullo
l'effetto.
Cio` significa che in effetti comunque fai qualcosa (overhead)...
[1] Ti ricordo che Obj->Field viene risolto dal compilatore come
      (Obj.operator->())->Field
Ecco come fare
trova_oggetto() non deve piu` restituire un obj* ma
un nuovo tipo: TProxy*... dove:
class TProxy {
  public:
     TProxy(Obj* ObjPtr);
     Obj* operator->();
     operator Obj*();
  private:
     Obj* ObjectPtr;
     static Obj* Dummy;
};
Obj TProxy::Dummy = new Obj;
TProxy::TProxy(Obj* ObjPtr) : ObjectPtr(ObjPtr) {}
// Se il puntatore tornato da trova_oggetto() e` nullo
// l'operatore -> restituisce l'indirizzo di un oggetto costruito
// apposta per rendere vane (ma non nulle) le operazioni successive
Obj* TProxy::operator->() {
   return ObjectPtr? ObjectPtr: Dummy;
}
TProxy::operator Obj*() {
  return ObjectPtr;
}
 
Riflettendoci un po' penso che non sia difficile capire come funzioni
(se funziona ndr) la cosa.
L'operatore di cast e` necessario nel caso in seguito alla chiamata a trova_oggetto()
ti serva effettivamente il puntatore (Es Obj* tmp = pippo.trova_oggetto(pluto))
Cosa potrebbe non funzionare?
Il compilatore potrebbe non essere capace di capire cosa fare per
pippo.trova_oggetto(pluto)->inserisci_valore(10);
ovvero se convertire a Obj* il valore restituito da
trova_oggetto() e poi applicare operator->, oppure
applicare subito TProxy::operator->()
Io mi aspetto che scelga la seconda strada perche`
operator-> ha precedenza maggiore del cast.
Il costo di questa soluzione e` dato principalmente dal
fatto che comunque indipendentemente dal valore
tornato da trova_oggetto() il programma fa sempre
qualcosa (eventualmente operazioni inutili), pero`
tu puoi sempre e comunque scrivere
pippo.trova_oggetto(pluto)->inserisci_valore(10);
Puoi diminuire questo overhead usando inline...
******Secondo me comunque il gioco non vale la candela..******
tanto vale utilizzare una funzione che esegua il test e
quindi in caso di successo esegua la chiamata a
inserisci_valore()
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------

[...]
Puoi usare un pattern di nullobject, facendo si' che la tua trova_oggetto,
in caso non.. trovi, non ritorni NULL, ma un puntatore (o un 
reference) ad un oggetto (di solito statico alla classe base) che implementa 
tutti i servizi in modo ..null, limitandosi di solito a segnalarti l'errore. 
Quest'oggetto si chiama "implementazione nulla". Ci sono varianti con diversi 
nullobjects a livelli diversi di gerarchia (per gestire cast dinamici etc) ma 
l'idea resta quella.
Il vantaggio e' che non hai l'appesantimento dell' "if ((tmp=...)!=NULL) etc" 
e il codice rimane piu' leggibile; inoltre dal punto di vista della 
manutenzione e'anche piu' facile definire o sostituire un comportamento 
standard per il caso "nullo".
Ovviamente lo svantaggio e' che devi scrivere e usare il codice per tale 
oggetto.
Saluti,
 
 Cris 

in caso non.. trovi, non ritorni NULL,
E` un pattern di cui non ho mai sentito parlare, sai dove posso trovare
ulteriori informazioni?


Ti rigrazio della dritta infatti pensavo di fare q.cosa di simile.
Rimane un problema.
il metodo trova_oggetto fa parte di una lista generica <template>
che non conosce l'oggetto (fra l'altro conosce solo un puntatore
generico alla classe base); la lista quindi rende per forza un NULL.
E' per questo che volevo fare un overload dell'operatore ->
in modo da poter rendere un oggetto (dummy) che assorbirebbe il
valore passato senza causare problemi durante l'esecuzione.
Ringrazio chi ha risposto e chi mi aiutera' ancora.
Ciao Fabrizio.
P.S: Se mi dici dove posso trovare anche in rete la documentazione
sui nullobject....

sui nullobject....
PS: Rispondo a te e a Paolo.
In rete, non saprei. Ricordo perfettamente di aver letto il pattern qualche 
anno fa, e da allora l'ho applicato moltissime volte... tuttavia, ho dato 
un'occhiata al mio fido "Design Patterns", convinto di trovarcelo e.. non 
c'e'! :) Sinceramente non mi ricordo, dato che ora vivo in Norvegia ma ho 
ancora un bel po' di libri a Milano. Ricordo che veniva chiamato Null Object, 
e (of course) aveva anche altri nomi. Forse su "CORBA Design Patterns" di 
Mowbray&c (quello con la copertina verde :) ma non ci giurerei perche' non ce 
l'ho sottomano.
Oh.. mentre scrivevo ho fatto una veloce ricerca in rete, ed eccoti qui:
http://home.att.net/~bwoolf/Null_Object/Null_Object.htm
Ho dato una scorsa e pare proprio lui.


http://home.att.net/~bwoolf/Null_Object/Null_Object.htm
Grazie mille...



Grazie mille    Fabrizio