Newsgroup: it.comp.lang.c++
Titolo: copia di un ogetto bit a bit

Saluti.
Vorrei sapere se questo codice è legale in C++
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
typedef std::vector<int> ivec;
using std::ostream;
using std::cout;
using std::cin;
ostream& operator<<(ostream& out, const ivec& v)
{
  out << "{";
  std::copy(v.begin(), v.end(), std::ostream_iterator<int>(out, ", "));
  return out << "\b\b}";
}
int main()
{
  cout << "sizeof(ivec): " << sizeof(ivec) << "\n\n";
  int _a[] = {1, 2, 3, 4, 5};
  ivec* a = new ivec(_a, _a + 5);
  cout << "\n\na = " << *a << '\n';
  ivec* a2 = static_cast<ivec*>( malloc(sizeof(ivec)) );
  // --- COPIA a in a2 (ma solo la locazione di memoria!) ---
  memcpy(a2, a, sizeof(ivec));
  cout << "a2 = " << *a2 << '\n';
  a2->push_back(-5);
  cout << "\na = " << *a << '\n';
  cout << "a2 = " << *a2 << '\n';
  // --- RILASCIA LE RISORSE ---
  delete a;
  free(a2);
}
Sia con il BCC5.5 che con il DJGPP funziona come voglio!
Grazie  Manlio Perillo

 wrote in message
8mk6bp$mr9$1...@nslave1.tin.it...



Non è detto.
Il fatto che il programma funzioni può essere legato al compilatore usato.
Io chiedevo se il programma è legale secondo quando specifica lo Standard per il C++.
Ad esempio per lo standard alcuni comportamenti sono indefiniti o dipendenti dalla implementazione
e non sarebbe saggio farci affidamento
Saluti  Manlio Perillo

 
Non so cosa tu intenda per "legale".  
Di certo copiare bit per bit un oggetto che non è POD e usare la copia
come fosse un oggetto che sia stato inizializzato da un costruttore è una
buona tecnica per creare disastri immani (anche particolarmente difficili
da debuggare).
Nel tuo caso, se il codice non produce disastri è solo un caso.
Col mio compilatore li produce eccome.
Però la curiosità è tanta: quali esigenze o specifiche ti hanno portato a 
concepire questo codice bizzarro? Quali vantaggi volevi ottenere?
L'unica cosa che riesco a immaginare è che l'intenzione fosse quella di
ottenere dei vector con buffer condiviso...
Max


 ... ergo é legale in C++
Il fatto che un particolare compilatore non dia problemi con un certo
codice non garantisce in generale che tale codice sia conforme allo
standard. Solo il documento ufficiale fa testo al riguardo.
EMG

concepire questo codice bizzarro? Quali vantaggi volevi ottenere?
Leggendo alcune implementazioni della funzione resize di std::vector
quello che si fa è:
allocare nuova memoria
copiare i vecchi oggetti nella nuova memoria
distruggere gli oggeti nella vecchia memoria
deallocare la vecchia memoria
Tutto questo non rispecchia quello che si vuole fare:
un semplice trasferimento di un oggetto da una locazione di memoria ad un'altra.
Perchè allocare nuove risorse se queste sono state già allocate nei vecchi oggetti?
Manlio Perillo

 Perchè allocare nuove risorse se queste sono state già allocate nei vecchi oggetti?
In generale ci sono seri problemi con questo. Prima di tutto una volta
copiata la zona di memoria non puoi sperare di poter usare entrambi gli
oggetti, giacchè entrambi riterranno di poter disporre delle risorse
allocate e interferiranno l'uno con l'altro (quindi, ad es, il distruttore
dell'oggetto originale farà disastri). In secondo luogo, il valore di this
potrebbe giocare un ruolo importante nel funzionamente interno
dell'oggetto, che, ad es, potrebbe usarlo o averlo usato per registrare se
stesso in qualche altro oggetto (che so? un event manager, ad es.)  o per
trasmettere un riferimento a se stesso a qualche risorsa che crea e via
dicendo.
Se c'è l'esigenza minimizzare l'allocazione di risorse quando un'oggetto
viene copiato, normalmente si ricorre a tecniche di reference-counting e
copy-on-write.
Max

 dell'oggetto originale farà disastri).
E' chiaro che tale oggetto non deve essere usato.
Infatti in una implementazione di resise viene distrutto.
Con il mio metodo viene semplicemente sovrascritto.
Manlio Perillo

Con il mio metodo viene semplicemente sovrascritto.
Il problema e' concettuale. Un oggetto NON e' un'area di memoria.
Per comprendere un caso pratico immagina un oggetto "cella di
spreadsheet" e che il suo indirizzo sia noto a qualcuno che
la interroga (supponiamo per aggiornare un grafico).
Cosa succede se sposti la cella senza "avvisare" il grafico ?
Un altro caso comune e' avere tutti gli oggetti di una
determinata classe inseriti in una struttura dati di classe
(es. una lista oppure una mappa). Questi oggetti vengono
creati e (all'insaputa di chi li crea) si registrano nel
loro club.
Cosa succede quanto tu sposti i bit della rappresentazione ?
Chi aggiorna la registrazione ?
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 Perchè allocare nuove risorse se queste sono state già allocate nei vecchi oggetti?
Perchè la memoria di uno std::vector deve essere consecutiva.

 wrote in message 398FF07F...@libero.it...

Per risorse intendevo quelle inizializzate dal costruttore di una classe, non la memoria
Manlio Perillo

Perchè la memoria di uno std::vector deve essere consecutiva.
Credo di aver letto da qualche parte che in realta' questa non e'
un requisito di std::vector. Attualmente tutte le implementazioni
note offrono questa garanzia (e difficilmente si vede come
potrebbero garantire un tempo di accesso costante altrimenti) e
inoltre la cosa risulta molto utile quando si deve passare un
vettore ad una funzione che non conosce std::vector<T> (magari
si tratta di una funzione C). Se la memoria e' contigua allora
si possono passare dei T* invece che degli iterator.
Se non ho capito male nella prossima versione dello standard
probabilmente si richiedera' effettivamente che la memoria di
std::vector sia contigua in modo da evitare possibili problemi.


 Per risorse intendevo quelle inizializzate dal costruttore di una classe, non la memoria
Ah, forse adesso ho capito. Intendi fare una implementazione di resize che avendo una classe
così:
class A
{
    int dimarray;
    int* array;
public:
    A(int dim) : dimarray(dim) { array = new int [dimarray]; }
    ~A() { delete [] int;}
    A (const A& obj)
    {
        dimarray = obj.dimarray;
        array = new int [dimarray];
        memcpy(array, obj.array, dimarray);
    }
};
eviti di riallocare un nuovo array e copii solamente l'indirizzo del vecchio array, perchè
tanto il vecchio oggetto verrà distrutto (in realtà non si puo chiamare il distruttore,
verrà semplicemente liberato il vecchio spazio di memoria). Interessante.
L'unico dubbio è se per la classe è stato definito un costruttore di copia che fa qualcosa
di "strano": nel caso del resize l'oggetto viene copiato e quindi viene chiamato il
costruttore di copia, nel tuo caso no. Bisogna vedere quale delle due interpretazioni sia
quella "giusta" (del resto l'oggetto è sempre lo stesso e viene semplicemente spostato in
memoria, quindi concettualmente non c'è bisogno del costruttore di copia, che invece
rappresenta il concetto di voler creare un oggetto identico all'originale, mantenendo
integro e valido l'originale stesso).


Esatto!
La mia implementazione per questa idea era usare memcpy per copiare il blocco
di memoria.
Tuttavia vorrei porre questa idea come estensione per lo Standard.
Ossia una funzione built-in che faccia esattamente questo.
Se si ha un vector<X> dove il costruttore di X alloca, ad esempio, 1 MB di memoria,
che senso ha allocare un'altro 1MB di memoria e cancellare la vecchia se poi alla
fine si vuole solo 'trasferire' un oggetto in una nuova locazione di memoria?
Tuttavia credo che riposterò la mia idea più avanti, a Settenbre;
infatti alcuni esperti di questo gruppo (come Alex Martelli) credo siano in
vacanza.
Vorrei avere anche un loro parere.
Manlio Perillo

La tua idea non e' accettabile perche' in C++ l' *unico* modo valido di
copiare un oggetto e' chiamarne il costruttore di copia. Tu dai per
scontato che una volta costruito un oggetto, non ci sia interesse a
"ricostruirlo" se lo si vuole spostare. In generale non e' detto che sia
cosi', ad esempio se il costruttore modifica membri statici della
classe.
Piuttosto che cercare di far diventare vector qualcosa che non e', forse
faresti meglio a studiare gli altri contenitori e vedere se non ce n'e'
uno piu' adatto a cio' che vuoi fare. In particolare piu' adatto ad
essere esteso dinamicamente.
Un'ultima cosa: il C++ attuale e' il frutto del lavoro di piu' di
vent'anni di alcune tra le migliori teste dell'informatica mondiale; e'
difficile che un problema come quello che ti poni sia frutto di una
semplice "svista".
Ciao,
Nicola

 classe.
C'è un eccezione importante a questa regola, definita come ottimizzazione
consigliata dalla standard:
A obj = A(5);
in questo caso ci si aspetterebbe che venisse costruito un oggetto
temporaneo e passato al costruttore di copia per costruire obj, in realtà la
precedente istruzione dovrebbe venire compilata (seguendo l'ottimizzazione
consigliata) come se si fosse scritto:
A obj(5);
in quanto l'oggetto temporaneo non serve, teoricamente, a nulla e verrà
scartato.
Il Visual C++ 6.0 chiama il costruttore di copia, ma Martelli, in un thread
di un mesetto fa, mi spiegava che e' perfettamente lecito secondo lo
standard, saltare il costruttore di copia.
La motivazione logica e' la stessa del resize in un vector: non si vuole
creare un nuovo oggetto da uno esistente, ma "spostarlo". Mettiamo ad
esempio che una classe abbia un membro statico che tenga il conto di tutte
le istanze, nel costruttore di copia si metterebbe un incremento di questa
variabile, e copiare l'oggetto senza chiamare il costruttore di copia
porterebbe dei problemi (un errato computo delle istanze). Nel caso
precedente però non viene creato un nuovo oggetto dal punto di vista logico,
e quindi può essere ammissibile saltare il costruttore di copia (ma questo
non sta sicuramente a me deciderlo).
Ciao Fbf.

 Vorrei avere anche un loro parere.
Prova magari a postare la tua idea anche sul gruppo internazionale del c++, magari c'è qualche
"controindicazione" che gli esperti potrebbero farti notare.
Ciao Fbf.

 Ciao Fbf.
Ci sono due differenze sostanziali: nel tuo esempio hai la certezza che
l'oggetto originale della costruzione non ottimizzata non subisca
manipolazioni tra la costruzione e lo "spostamento" e l'oggetto finale
e' comunque risultato di una chiamata ad un costruttore/operatore di
assegnamento; in generale la prima non e' necessariamente valida per un
vector e la seconda e' violata dalla proposta di cui si sta discutendo.
C'e' un altro aspetto poi da tenere in considerazione, che renderebbe
ulteriormente complessa la realizzazione corretta di quanto Manlio
propone, ed e' che i contenitori della libreria standard sono
parametrizzati rispetto alla strategia di allocazione.
Ciao,
Nicola
Ciao,
Nicola

 nuovo oggetto da uno esistente, ma "spostarlo".
La differenza è che nell'ottimizzazione in questione non c'è nessun
oggetto che viene costruito in un punto e poi spostato "fisicamente" in un
altro. Nell'implementazione di resize proposta da Manlio, gli oggetti
nascerebbero in un posto e poi cambierebbero sileziosamente locazione. In
altre parole vedrebbero il valore di this cambiare da un momento
all'altro. Mi pare che questo possa essere perfettamente indolore in certi
casi e avere invece effetti devastanti in altri, a seconda di quello che
gli oggetti fanno e di come interagiscono con il resto del sistema.
MM

 porterebbe dei problemi (un errato computo delle istanze).
Non credo.
L'implementazione di vector usa la memoria allocata con operator new che non chiama
i costruttori.
Quindi sta alla implementazione inizializzare la memoria di volta in volta quando serve.
Nella funzione reserve un oggetto A viene copiato in una locazione di memoria x con
new(x) T(A) e poi distrutto A->~T().
Quindi se hai dei membri statici per effettuare ad esempio un reference-counting
l'effetto netto č: count = count + 1 - 1 = count!
Quindi l'effetto netto č proprio quello di SPOSTARE l'oggetto A.
Mi sembrerebbe alquanto strano se usando vector<X> dove X č una classe
che effettua cose strane con i membri statici, quando vado a chiamare reserve
i membri venissero modificati in un modo imprevedibile.
(Questo vale ancora di piů con insert, ci sono diversi modi di spostare degli oggetti;
 cio comporterebbe che ho degli effetti imprevedibili sui miei membri statici?)
Manlio Perillo

 Ciao Fbf.
Tempo fa postai un messaggio ma non č mai apparso ne ricevetti risposte.
Manlio Perillo


A questo punto mi sorge la domanda:
per quale motivo i compilatori C++ non vengono "certificati" come invece si
fa per i compilatori Ada95  ?
( dove la certificazione garantisce che il compilatore sia conforme allo
standard ovviamente e non altro )

Quindi l'effetto netto č proprio quello di SPOSTARE l'oggetto A.
Il punto e' proprio questo. Per "spostare" un oggetto e' NECESSARIO
chiamare il COSTRUTTORE di copia nella nuova posizione (creando una
nuova istanza) e chiamare il DISTRUTTORE della vecchia istanza.
In alternativa si potrebbe costruire un nuovo oggetto con un
COSTRUTTORE DI DEFAULT e poi usare l'operatore di ASSEGNAZIONE.
Certamente NON e' consentito spostare "i bit" dell'oggetto.
Il problema e' che *altri* potrebbero conoscere l'indirizzo della
vecchia istanza. Se tu sposti le cose con memcpy i poveretti
potrebbero restare delusi nel non trovare piu' gli oggetti
nel posto in cui dovevano essere.
Avere "altri" che conoscono l'indirizzo di una istanza
implica che la comunicazione dell'indirizzo all'atto della
creazione e della fine validita' all'atto della distruzione
sono due compiti di cui si devono fare carico costruttore e
distruttore dell'oggetto stesso.



Forse perchè il C++ è un linguaggio libero (a differenza, ad esempio, del Java).
Quindi chiunque può creare un compilatore che si basi sulle specifiche C++
e farne poi quello che gli pare.
Manlio Perillo.


Altri chi?
Il tutto è gestito internamente da vector.
Inoltre anche se chiami il distruttore di un oggetto e poi vi accedi succede un disastro!
int* a = new X;
delete a;
cout << "a = " << *a;   // disastro.
Manlio Perillo

 nel posto in cui dovevano essere.
In effetti č un po' quello che succede quando l'iteratore di un
contenitore diventa non piů valido.
EMG

 Il tutto è gestito internamente da vector.
E che se sai? Puoi saperlo solo se conosci quallo che gli oggetti fanno,
diversamente non sei garantito che altre parti del sistema non finiscano
per usare l'indirizzo degli oggetti.
Esempio: un oggetto al momento della creazione si registra (usando il
proprio indirizzo) presso, ad es., un gestore di eventi, il quale
successivamente invocherà qualche suo metodo tramite un puntatore (si
tratta di un pattern abastanza comune). Al momento della distruzione
l'oggetto notifica al gestore di eventi la sua imminente morte. Tutto
questo funziona in C++ perchè si può fare affidamento sulla stabilità
dell'indirizzo di un oggetto.
Max

 wrote in message Pine.LNX.4.10.100081...@c64.comm2000.it...

L'obiezione è corretta. Ma come ho già scritto prima il problema è questo:
Quando usi resize l'IDEA è di spostare oggetti da una locazione di memoria ad
una nuova.
E' un DATO di FATTO che in C++ questo venga 'simulato' copiando il vecchio
oggetto in uno nuovo e distruggendo quello vecchio.
Supponendo di avere una classe che fa quello che dici, quando viene creato
un nuovo oggetto questi si registra con il suo indirizzo (che è univoco)
mentre quando il vecchio oggetto viene distrutto questi notifica la
sua imminente morte.
Ma allora cosa cambia se il nuovo oggetto è LA STESSA COSA del vecchio?
(è questo che voglio con transfer)
E' come se l'oggetto non fosse mai stato spostato.
Manlio Perillo

 Ma allora cosa cambia se il nuovo oggetto è LA STESSA COSA del vecchio?
Non lo e' perche' si trova in una locazione di memoria diversa e come ti
e' stato fatto notare un oggetto che manipola il proprio indirizzo deve
poter contare sul fatto che questo non cambi per tutta la sua esistenza.
Ciao,
Nicola

 > variabile, e copiare l'oggetto senza chiamare il costruttore di copia


 parametrizzati rispetto alla strategia di allocazione.
Vero, effettivamente mi sa che questo, assieme al fatto che cambia l'indirizzo di
this come faceva notare Max M., stronca le gambe all'idea di copiarlo bit a bit.
Ciao Fbf.

E' come se l'oggetto non fosse mai stato spostato.
Con la sola decisamente non trascurabile differenza che
l'osservatore non e' stato informato del cambio di indirizzo.
Quindi il disinformato (ad esempio) inviera' il suo messaggio
a un oggetto che e' stato sovrascritto da altra roba perche'
qualcuno ha voluto recuperare la memoria senza chiamare il
distruttore.
E quindi la segnalazione di surriscaldamento non arrivera' al
gestore allarmi ma fara' invece piantare il programma o peggio
fara' fare cose sbagliate ma subdolamente non evidenti.
E quindi il nocciolo fondera'.
E quindi molte persone moriranno.
E qualcuno pensera' che sia stato un atto terroristico.
E mettera' la legge marziale.
E ci saranno sommosse, sino alla guerra civile.
E le nazioni confinanti inizieranno a farci la guerra
interessate spartirsi il potere in uno stato che ha perso
il controllo della situazione.
E le altre nazioni si coalizzeranno e si faranno la
guerra perche' questo porterebbe a cambiamenti
inaccettabili degli equilibri politici ed economici.
E molte piu' persone moriranno.
E infine nel caos qualcuno premera' il bottone che
e' stato costruito per non essere mai premuto.
E il mondo per come lo conosciamo finira' lasciando sul
pianeta solo qualche muschio resistente alle radiazioni(1)
Non era meglio chiamare il costruttore di copia e il
distruttore come richiesto ?
Andrea
(1) esempio di conseguenze liberamente tratto da un
    testo sui rischi dei memory leak letto da qualche
    parte su 
www.perl.com
.


Dev'essere a causa del tuo esempio che tanto l'SDK di Java quanto il BCB
recano nella licenza d'uso un'indicazione che "tale strumento non e'
adatto alla gestione di sistemi da ci dipenda la vita di persone, come
centrali nucleari, ecc."...
Ciao,
Nicola

   c'e' un errore.
Ben detto! Ed è valido per tutti i linguaggi di programmazione che io
conosca, non per i soli C e C++.
EMG


Hai perfettamente ragione. A questo non avevo pensato.
Grazie per avermelo fatto notare.
Peccato, l'idea di non dover riallocare, copiare e deallocare risorse ad ogni spostamento
mi piaceva.
Manlio Perillo


Forse ti era sfuggito il fatto che la mia era solo una implementazione del 'trasferimento'
di due oggetti.
Quello che speravo era che l'idea fosse valida e che chi di dovere implementasse
un meccanismo sicuro per effettuare la cosa.

...non era meglio che l'uomo non fosse mai esistito sulla faccia della Terra?
Manlio Perillo