Newsgroup: it.comp.lang.c++
Titolo: domanda su auto_ptr

ma perche' il comitato standard ha approvato il meccanismo di scambio 
di proprieta', invece del reference counting a-la basic_string?
possibile per questioni di performance? per togliere l'overhead di 
aggiornare un contatore su assegnazione, copia, costruzione e 
distruzione?
dato che stavo pensando di implementarne una versione da usare nei 
container della lib. std (notoriamente impossibile con gli auto_ptr), 
volevo sapere se c'e' qualche motivo recondito che mi sfugge, o se per 
mantenere la trasparenza rispetto ai puntatori si e' scelto di non 
imporre il pur minimo overhead (tra l'altro dell'ordine di grandezza 
del flag di ownership, a mio avviso...)
grazie della disponibilita'

 del flag di ownership, a mio avviso...)
Se sei interessato alle prestazioni associate alle varie strategie, questo
documento presenta i risultati di alcuni test effettuati con MSVC++ e gcc.
 Smart Pointers Timings
 
http://www.boost.org/libs/smart_ptr/smarttests.htm
Max

 MSVC++ e gcc.
grazie della dritta, ma la domanda rimane...

del flag di ownership, a mio avviso...)
Una questione e' il fatto di poter far puntare un oggetto
generico da un auto_ptr senza essere ad esempio obbligati a
derivare l'oggetto gestito da una base "reference_counted".
La seconda questione e' che auto_ptr e' a overhead *zero*
(non "piccolo") in termini di spazio e ha un basso overhead
in termini di tempo (le operazioni di assegnazione e copia
semplicemente azzerano chi perde l'ownership dell'oggetto
puntato)...
Io personalmente preferisco i puntatori reference-counted
intrusivi (sono semplici, ragionevolmente efficienti e non
costano molto -- un int per oggetto puntato).
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 derivare l'oggetto gestito da una base "reference_counted".
non ho capito se ti riferisci al polimorfismo dell'oggetto puntato.
se uso uno smart_ptr con reference count, cosa mi puo' comportare?
puoi chiarire il discorso, magari con un esempio? (sono un po' duro di 
comprendonio, lo so)

 wrote in message news:3865529.N...@tin.it...

Prova a fare uno smart pointer con reference count che possa puntare a un
oggetto qualunque (per esempio un double) e capirai subito cosa intendeva
Griffini.
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

 intendeva Griffini.
In realtà esistono tecniche per implementare smart pointer non intrusivi
che possono puntare a oggetti qualsiasi (compresi tipi built-in).
Nella mia prima reply in questo filone ho citato boost::shared_ptr<T>. Sul
sito di Boost (
www.boost.org
) trovi anche linked_ptr<T> che, usa una
stretegia diversa, con vantaggi e svantaggi rispetto all'altra (ad es, non
richiede allocazione dinamica del contatore, ma le operazioni di copia
sono leggermente più "costose").
Max

 che possono puntare a oggetti qualsiasi (compresi tipi built-in).
Si. Ma non a overhead zero come nel caso di auto_ptr.


sono leggermente più "costose").
Per un po' sono stato iscritto alla mailing list di boost (ho dovuto
rinunciare per mancanza di tempo) ma gli smart ptr gli avevo visti.
Quello che pero' ho detto io e' che, personalmente, preferisco in
questi casi le soluzioni intrusive, semplici ed efficienti a quelle
non-intrusive, complicate e leggermente inefficienti.
Io scrivo *programmi* e ho certi obbiettivi, ovviamente il discorso
cambia per chi scrive librerie generiche (in cui il riuscire a non
essere intrusivo e' molto importante).
L'OP comunque a quanto pare credeva che auto_ptr avesse qualche
overhead in termini di spazio (cosa falsa; forse in qualche
ompilatore questo puo' accadere... ma non e' una necessita' e
sui compilatori che conosco un auto_ptr occupa quanto un ptr e
l'efficienza e' praticamente la medesima).


 praticamente la medesima).
Forse mi sfugge qualcosa, ma come puň un auto_ptr non richiedere un po'
piů di spazio di un puntatore? In tutte le implementazioni che ho visto,
auto_ptr contiene anche un membro dati booleano che segnala l'ownership
del puntatore.
Max

del puntatore.
L'implementazione presentata in "The C++ Programming Language" non ha
nessun campo booleano in piu'. Per indicare che un auto_ptr perde
l'ownership basta non farlo puntare piu' a niente...
... esempio semplificato e non provato ...
  template<class T>
  class auto_ptr
  {
    private:
      T *a;   // Puntatore vero e proprio
    public:
      explicit auto_ptr(T *aa=0) : a(aa) { }
      ~auto_ptr() { delete a; }
      auto_ptr(auto_ptr<T>& other) : a(other.a)
      { other.a=0; }
 
      auto_ptr& operator=(auto_ptr<T>& other)
      {
        delete a; a=other.a; other.a=0;
        return *this;
      }
      T* get() const { return a; }
      T* release()   { T* r=a; a=0; return a; }
      T* operator->() const { return a; }
      T& operator*()  const { return *a; }
      ...
  };
La gestione dei "const" complica un po' le cose come
anche la gestione delle assegnazioni puntatore a base
e puntatore a derivata.
nota che costruttore di copia e operatore di
assegnazione accettano reference non-const perche'
devono "togliere" l'ownership all'altra istanza e
lo fanno azzerandone il puntatore.


 l'ownership basta non farlo puntare piu' a niente...
sullo standard si appoggia ad una classe auto_ptr_ref: 20.4.5

sullo standard si appoggia ad una classe auto_ptr_ref: 20.4.5
Anche Stroustrup in TCPPPL 3ed (14.4.2) che ho indicato a Max.
E questo secondo te cosa dovrebbe implicare ? Che un auto_ptr
e' piu' grande di un puntatore normale ?


 l'ownership basta non farlo puntare piu' a niente...
Ah, certo, ora capisco. Per qualche ragione ero erroneamente convinto che
un'operazione di copia non dovesse invalidare immediatamente l'auto_ptr,
ma soltanto bloccare il delete nel distruttore. (Morale: mai credere di
poter intuire i requisiti dello standard esaminando qualche
implementazione.)
Grazie,
Max

       T* release()   { T* r=a; a=0; return a; }
Se non sbaglio, dovrebbe essere
T* release()   { T* r=a; a=0; return r; }
Giusto? Altrimenti a cosa serve r?
EMG

Giusto? Altrimenti a cosa serve r?
Whoops... errore di battitura... la cosa giusta e' ovviamente
"return r" come hai fatto notare tu; quello che volevo fare
era tornare il valore di a pero' azzerando a.
Mi scuso per l'errore ... spero che comunque il senso del
codice fosse comunque comprensibile (NB: quella non voleva
essere una implementazione corretta di auto_ptr... mancano
un po' di cose per arrivarci... l'idea era solo di mostrare
un esempio di smart-ptr con la stessa semantica e con
overhead di spazio potenzialmente nullo).


implementazione.)
Forse mi sbaglio (sono approdato da poco al C++ e non conosco
l'evoluzione del linguaggio) ma dall'implementazione VC++ che ho
osservato credo di intuire che forse quella che descrivi era
la semantica piu' comune di auto_ptr pre-standardizzazione.
Ho letto che la logica di auto_ptr e' stato modificata poche
ore prima della chiusura del documento finale (letteralmente
poche ore anche se sulle novita' i membri del comitato erano
gia' stati informati precedentemente): forse questo e' stato
uno dei (non piccoli) cambiamenti apportati.
Chiaro che se un auto_ptr puo' avere o meno l'ownership e
sempre e comunque puntare a qualcosa allora i bit di un
puntatore non bastano. Per definizione.


 wrote in message
news:3b308a4...@news.interbusiness.it...

Non so se lo standard e' cambiato all'ultimo minuto o meno. Ma so che lo
standard dice che dopo la chiamata a release l'auto_prt deve puntare a NULL. E
so che assegnando un auto_ptr ad un altro il destinatario deve chiamare release
sul sorgente e quindi diventa proprietario dell'indirizzo ritornato. Pertanto
l'implementazione che dopo l'assegnamento setta un flag per evitare la delete in
distruzione ma non setta il puntatore a NULL non 'e conforme.
In altre parole un auto_ptr si trova in due soli possibili stati: ha l'ownership
di un oggetto e ne mantiene l'indirizzo, non ha l'ownerschip e non ne mantiene
l'indirizzo.


 e' piu' grande di un puntatore normale ?
se proprio vuoi una risposta sensata diversa dal po' esse verdoniano...
in generale, dipende se il compilatore che usi ottimizza la void class.

in generale, dipende se il compilatore che usi ottimizza la void class.
Forse stiamo guardando due implementazioni diverse di auto_ptr.
In quella che ho visto io nella classe auto_ptr si
definisce una sotto-classe auto_ptr_ref per questioni
tecniche relative al ritorno di un auto_ptr da una
funzione ma in ogni caso non si mette nessuna
*istanza* di auto_ptr_ref in auto_ptr.
L'unico membro dati dichiarato da auto_ptr<T> e' un
T* e nient'altro. Ovviamente che la classe risultante
auto_ptr sia grossa esattamente quanto un puntatore e'
un altro discorso e credo dipenda dall'implementazione.
La presenza dell'ottimizzazione di dimensione delle
classi vuote che si applica alle classi base non centra
proprio nulla il questo caso (auto_ptr non contiene
nessun auto_ptr_ref e non e' una classe derivata).


l'ownerschip e non ne mantiene l'indirizzo.
Infatti. Solo che dopo il commento di Max sono andato a
vedere cosa fa il VC++ 6.0 e, visto che non credo che Plaguer
sia l'ultimo dei rimbambiti, sono portato a pensare che la
forma scelta per l'implementazione sia sensata ma semplicemente
vecchia (pre-standard).
Questo collegato a quanto ho letto da diverse parti riguardo
al fatto che la semantica di auto_ptr e' stata cambiata
appena prima di chiudere il documento definitivo.
Andrea
PS:
  Per chi e' interessato ho trovato questo link:
 
http://www.awl.com/cseng/titles/0-201-63371-X/auto_ptr.html
  Descrive la tormentata storia di auto_ptr... da cui si vede
  che la versione di cui parlava Max (e che e' attualmente in
  uso nel VC++ 6) e' probabilmente una "versione 2".
  Certo che quei miseri 32 bit (se i puntatori sono 32 bit)
  hanno fatto sudare non poco (e non e' nemmeno detto che
  quei poveri malcapitati abbiano finito di sudare) :D
  Mah... certo che ste cose fanno riflettere... no ?


   Mah... certo che ste cose fanno riflettere... no ?
Si, e mi fanno stare ben alla larga dal C++ per le normali applicazioni
gestionali...
EMG

   quei poveri malcapitati abbiano finito di sudare) :D
Alla recente ACCU Spring Conference Nico Josuttis ha annunciato "en
passant" che auto_ptr e' stato definitivamente aggiustato, ma non ha
specificato in che senso e devo ammettere di non aver fatto ulteriori
ricerche.
Ciao,
Nicola


Eppure continuo a pensare che C++Builder possa essere uno strumento
adatto a questo tipo di problemi.
Ciao,
Nicola

standard dice che dopo la chiamata a release l'auto_prt deve puntare a NULL. E
ahi ahi, finalmente di colgo in fallo! L'uso di NULL è deprecato :-)))
Anto
--
Antonio Cuni, 19 anni, studente
Brainbench C++ Master: 4.6, higher than 98% of all previous test takers.
C Language: 3.32, higher than 79% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

 ricerche.
Probabilmente si riferisce ai defect report riguardanti auto_ptr:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#127
	Mauro

 Forse stiamo guardando due implementazioni diverse di auto_ptr.
ma pur sempre conformi allo standard...
e la void class dovrebbe funzionare anche per classi membro vuote o no?


No. Lo standard prevede esclusivamente l'ottimizzazione per classi base
vuote. Altrimenti ci sarebbero membri con lo stesso indirizzo. In ogni
caso auto_ptr come definito dallo standard non necessita di membri
aggiuntivi al puntatore e dubito molto che esistano implementazioni
conformi che ne abbiano.
Ciao,
Nicola

 ahi ahi, finalmente di colgo in fallo! L'uso di NULL è deprecato :-)))
Era MAIUSCOLO per EVIDENZIARE il valore assunto dal puntatore :-) Nessun
riferimento alla omonima MACRO :-) Il valore null (evidenziato NULL) esiste. La
costante corrispondente e' 0. In questa accezione null (in italiano nullo) e'
corretto. L'uso della MACRO chiamta 'NULL' come in C e' deprecato.
:-)))
Fiew. Salvo in corner! :-))
:-))


passant" che auto_ptr e' stato definitivamente aggiustato,
Hehehe... "definitivamente" ...


 Hehehe... "definitivamente" ...
Lui ha detto cosi'. D'altronde quando gli avevo chiesto delucidazioni
circa la parte del suo libro relativa all'auto_ptr mi ha sostanzialmente
risposto (per quanto in modo piu' cortese) che ne aveva piene le tasche
dei problemi dell'auto_ptr :-)
Ciao,
Nicola

In realta' l'uso della macro NULL e' certamente un C-ismo, la
definizione del C++ e' piu' restrittiva, ma la macro in se non e'
affatto deprecata.
Ciao,
Nicola

 ha scritto nel messaggio
news:3B332BA5...@divalsim.it...


 io uso la costante 0L
Se per questo funziona anche false, per quanto risulti un po'
fuorviante.
Ciao,
Nicola

 fuorviante.
Anzi: tecnicamente funziona meglio false nel senso che e' piu' portabile di 0L.


Anzi: tecnicamente funziona meglio false nel senso che e' piu' portabile di 0L.
Perche' e' piu' portabile ?
...
(fra l'altro BCC55 non accetta la conversione false->null ptr)
Comunque questa e' un'altra cosa che non comprendo... io credo che
avrei preferito che solo il literal numerico 0 fosse accettabile
come null pointer invece di far accettare al compilatore
anche cose come:
   int *p = 6502 - 0x1966;
Qualcuno sa quali sono i motivi dietro questa scelta ?
Spero che non sia solo per la utilissima possibilita' di
scrivere qualcosa del tipo
   int *p = !   !  !   !  !     !
            !!  !  !   !  !     !
            ! ! !  !   !  !     !
            !  !!  !   !  !     !
            !   !   !!!   !!!!  !!!1;
come ha fatto Stroustrup in "The C++ Programming Language" :))
Andrea
PS:
  Io fra l'altro avrei capito di piu' l'introduzione di una
  nuova parola chiave ("null" ?) visto che si sono introdotte
  keyword anche per cose che personalmente ritengo meno
  essenziali (come "mutable").
  Io anche a causa dell'ambiguita' di "0" ho avuto problemi
  di comportamenti difformi fra diversi compilatori C++ anche
  con cose relativamente basilari come l'operatore ternario ?:


    int *p = 6502 - 0x1966;
Attento. Questa espressione e' valida perche' e' calcolata a compile time (e'
una "costant expression"). Lo stesso vale se scrivi:
int *p = reinterpret_cast<int*>(6502 - 0x1966);
Pero' al contrario di quello che molti pensano se tu scrivi:
int *p = reinterpret_cast<int*>(MyFirstProcessor() - 0x1966);
dove MyFirstProcessor() e' una funzione che dopo essersi fatta i cavoli suoi
ritorna un intero avente come valore 6502 (in decimale), sei a rischio. Il
codice compila e il puntatore viene assegnato. Ma non e' affatto detto che al
puntatore sia assegnato un null. Cosi' come:
int i=0;
int*p=0;
assert(reinterpret_cast<int*>(i)==0); // non garantito
assert(reinterpret_cast<int>(p)==0); // non garantito
assert(reinterpret_cast<int*>(i)==reinterpret_cast<int*>(0)); // non garantito
assert(reinterpret_cast<int>(p)==reinterpret_cast<int>(0)); // non garantito
assert(i==reinterpret_cast<int>(0)); // questo invece e' garantito
assert(p==reinterpret_cast<int*>(0));  // anche questo e' garantito
Inoltre:
int*pi=0;
char*pc=0;
assert(pi==reinterpret_cast<int*>(pc)); // garantito
assert(reinterpret_cast<char*>(pi)==pc); // garantito
assert(reinterpret_cast<int>(pi)==reinterpret_cast<int>(pc)); // questo NON e'
garantito


a compile time (e' una "costant expression").
E chi ha mai detto nulla ? E' chiaro che questo vale solo
per le constant expression... io mio dubbio era:
 
 "Perche' non si e' scelto invece di permettere solo
  la conversione da un *literal* a null pointer ?
  A cosa serve questa (buffa) flessibilita' aggiuntiva ?
  Qui prodest ?"
Andrea


i bool sono degli "integral types"... in particolare:
tra l'altro DJGPP accetta questo:
int a[true];
quindi il tipo bool dovrebbe essere un tipo intero, no?


   Qui prodest ?"
Nun saccio :-)


 quindi il tipo bool dovrebbe essere un tipo intero, no?
Mumble. Cominciate ad essere troppi a pensarla diversamente da me. Nicola,
Andrea e adesso tu. Comincio ad avere qualche dubbio. Ovvero che lo standard
ammetta come "costant expression of integral type" anche false di tipo booleano
ed eventuali enum.
Il fatto che un compilatore accetti qualche cosa non prova assolutamente nulla.
Ma proprio nulla.
Ma il fatto che 3 o 4 persone la pensino diversamente, pur non provando nulla,
mi fa venire un dubbio.
Forse forse bisognerebbe sentire qualche esperto.
Ma se li ammette allora il paragrafo sul bool citato da Andrea andrebbe
modificato: cosi' come e' scritto sembra implicare che bool si comporta come un
integral pur senza esserlo.


quindi il tipo bool dovrebbe essere un tipo intero, no?
Il BCC55 si comporta in modo strano... non ci sono problemi
a scrivere cose come
  int a[true];
pero' non e' ammesso
  char *p = false;
(che invece viene anche portato come esempio in "The C++
Programming Language" terza edizione [C.6.2.3]).
Mah...
Altro che discutere delle fanta-psico-meta-specializzazioni
parziali su parametri che sono puntatori a metodi template
della stessa classe di cui si sta definendo il template!
A volte ho l'impressione che si sia ancora in alto mare
con le *basi* (quantomeno nelle implementazioni correnti).


dato puntatore.
Il problema e' in che contesto viene valutata la constant
expression. Se sappiamo che stiamo cercando un puntatore
allora l'espressione "false" e' una espressione costante
accettabile (usa solo promozioni e dopo il calcolo vale zero).
Bah... certo che tutta sta storia del type safety per
introdurre il bool e poi avere cose come "int v[true];"
accettabili ...
Non era meglio tenersi le (chiare) regole del C ? :DDDD


bool a int :-) ).
ROTFLAS
rolling on the floor laughing AND STUDYING :-( 


Non era meglio tenersi le (chiare) regole del C ? :DDDD
sempre tenendo presente che il compilatore non fa testo, però mi
accetta anche cose del genere:
enum prova { X = false, Y = true, Z };
a Z viene dato il valore 2, quindi sembrerebbe che i bool vengano
convertiti ad interi.
Altra domanda sugli enum:
enum prova { X = 0, Y, Z = 1 };
con DJGPP sia Y che Z valgono 1. E' un bug?


   con cose relativamente basilari come l'operatore ternario ?:
Non posso dire di considerare l'operatore ?: come basilare, soprattutto
dopo aver sbirciato cosa ne dice lo standard. Altro che versione di
greco!
Ciao,
Nicola

 E fa bene.
Mi spiace contraddirti (non e' vero, mi piace un sacco :-), ma bool e'
un integral type secondo lo standard (3.9.1-7) e false e' quindi una
integral constant di valore zero. A norma di standard int * p = false;
e' un'istruzione valida.
Ciao,
Nicola

 con le *basi* (quantomeno nelle implementazioni correnti).
Anzi, probabilmente si e' piu' in alto mare con le basi che non con gli
aspetti piu' avanzati. Sospetto che questo dipenda dal fatto che il C
non sia una base di partenza adatta per un linguaggio con le ambizioni
del C++.
Ciao,
Nicola

 e' un'istruzione valida.
Infatti nel week-end sono andato a controllare lo standard (non ho ancora
provveduto all'installazione/estallazione :-) su questa macchina) e sono giunto
alla stessa conclusione. Come ho detto qualche istante fa in un altro post.
Quindi ritratto la ritrattazione :-(


 con DJGPP sia Y che Z valgono 1. E' un bug?
No. E' giusto.
