Newsgroup: it.comp.lang.c++
Titolo: Creazione dinamica di un vettore di stringhe

Ciao a tutti! Sto cercando di scrivere una funzione che mi crei e
restituisca un vettore di 2 stringhe. La prima cosa che ho fatto era
creare semplicemente un vettore di puntatori, e restituirlo, ma
giustamente non posso perché sarebbe variabile locale. Quindi ho
pensato di utilizzare new ed allocare la memoria necessaria. Questo
dovrebbe andare no? Ma come dichiare il mio vettore di 2 puntatori a
char?
Io avrei provato così:
char*** refmap = new char**;
Ma poi riesco a modificare solo la prima stringa quindi evidentemente ho
sbagliato qualcosa:
(*refmap)[0] = "str1";
(*refmap)[1] = "str1";
Mi sapreste dire se ho sbagliato qualcosa oppure se magari ho sbagliato
tutto? Come posso fare?
Grazie mille!

 restituisca un vettore di 2 stringhe. 
Intanto devi capire se le stringhe devono essere allocate dinamicamente 
oppure se hai solo bisogno di utilizzare stringhe costanti o statiche.
Il vettore, invece, non è necessario allocarlo dinamicamente, puoi anche 
passarlo direttamente sullo stack, in quanto occupa ben poca memoria.
Dunque potresti fare, ad esempio:
typedef char *myType[2]; // un array di due puntatori a char
myType myFun() {
	myType r;
	// il comando strdup alloca dinamicamente la memoria con malloc
	r[0]=strdup("prima stringa");
	r[1]=strdup("seconda stringa");
	return r;
}
PS. Ma il C++ cosa c'entra? Se usi il C++ perché non usi std::string?
E.

 char?
Nel XXI secolo si fa cosi':
#include <string>
#include <vector>
std::vector<std::string> > f() {
  std::vector<std::string> > v;
  v.push_back("pippo");
  v.push_back("pluto");
  return v;
}
Ciao,
Nicola

 PS. Ma il C++ cosa c'entra? Se usi il C++ perché non usi std::string?
Come cosa c'entra? Beh, non ho mai usato quella classe, per cui non
avevo voglia di andare tanto a leggermi come funzionasse. Tutto qui.
Comunque poi ho pensato fosse meglio quindi ho riscritto tutto.
Grazie ancora!

 Nicola
Invece che usare la classe vector ho usato un puntatore ad un array di
stringhe. Se ritorno un puntatore all'array ho sempre lo stesso
problema? Cioè, la variabile è sempre solo locale oppure no?
Grazie!

 problema? Cioè, la variabile è sempre solo locale oppure no?
Strana scelta... l'array di stringhe lo devi allocare dinamicamente, 
altrimenti il puntatore rimane impiccato.
E.

 problema? Cioè, la variabile è sempre solo locale oppure no?
Certo che hai il problema! Perche' ti vuoi fare male da solo? Usa la
libreria standard ogni volta che puoi, e quando non la usi assicurati
di avere un motivo valido per non farlo.
Ciao,
Nicola

 std::vector<std::string> > f()
Con copia del vettore nella fase di ritorno della funzione. E' una cosa 
che odio. Preferisco in questi casi passare sempre i "valori di 
ritorno" nei parametri d'ingresso.
void f(std::vector<std::string> >&);
-- 
Un libro da pausa caffè: 
http://research.microsoft.com/~daniel/uhh-download.html

 
ciao
Soviet_Mario

 void f(std::vector<std::string> >&);
Tendenzialmente anch'io. Pero' e' una forma di ottimizzazione
prematura, soprattutto se presentata ad un principiante. Senza contare
che molti compilatori sono in grado di eliminare la copia.
Ciao,
Nicola

 prematura, soprattutto se presentata ad un principiante.
Capisco il punto di vista e comprendo, però forse anche il principiante 
dovrebbe capire bene come funziona lo stack e i costruttori di copia. 
Non credo sia un dettaglio per programmatori "avanzati", puoi farti 
male a non sapere queste cose.
Al limite potrebbe anche non capire e seguire pedissequemente gli 
insegnamenti di quelli con più esperienza. Se poi acquisisce conoscenza 
può metterli in discussione e cercare la sua strada. Per questo motivo 
avrei fatto l'esempio con il vettore passato per parametro.
Per esempio io di prassi evito le copie di container, e metto i 
parametri const delle funzioni a sinistra, e quelli non const a destra. 
Ho una mia metodologia nei nomi delle funzioni, dei parametri e delle 
variabili. E così via.
Però mi sa che hai ragione tu, e che à meglio procedere per gradi 
nell'insegnare le cose. Mi sa anche che non sono bravo a trasmettere le 
mie poche conoscenze ad un novizio.
Aggiungo che per me con python e dotnet ti puoi fare male perché ti 
dimentichi il significato di parametro passato per riferimento, e 
valore di ritorno passato per copia. Ma quello è il prezzo da pagare 
con i linguaggi di un "certo" livello ;-)
Ora torno in bagno, l'influenza ha colpito anche me :'(


 nei parametri d'ingresso.
In realtà il vettore si trova già sullo stack, quando dichiari la 
variabile v. Colgo l'occasione per porre una domanda che mi sono sempre 
posto riguardo al linguaggio.
In un caso come questo:
T fun() {
	T x;
	return x;
}
la specifica del C++ garantisce che venga copiato x e poi distrutto 
oppure il compilatore può prendersi la libertà di ritornare direttamente 
l'oggetto x senza chiamare il distruttore e la copia?
E.

 l'oggetto x senza chiamare il distruttore e la copia?
La seconda. E' un'ottimizzazione lecita.
#include <iostream>
#include <ostream>
struct myobject {
  int value;
  myobject() : value(0) {
    std::cout << "Constructed myobject "
              << std::hex
              << reinterpret_cast<unsigned int>(this)
              << std::endl;
  }
  myobject(const myobject& o) : value(o.value) {
    std::cout << "Copied myobject "
              << std::hex
              << reinterpret_cast<unsigned int>(&o)
              << " into "
              << reinterpret_cast<unsigned int>(this)
              << std::endl;
  }
  ~myobject() {
    std::cout << "Destroyed myobject "
              << std::hex
              << reinterpret_cast<unsigned int>(this)
              << std::endl;
  }
};
myobject f() {
  myobject ob;
  return ob;
}
int main (int argc, char const *argv[])
{
  myobject ob = f();
  std::cout << ob.value << std::endl;
  return 0;
}
% ./copy_opti                      
Constructed myobject bfffef58
0
Destroyed myobject bfffef58
-- 
cc = callcc {|cc| cc.call cc}; cc.call cc

 La seconda. E' un'ottimizzazione lecita.
Sai che non me lo aspettavo? Avrei scommesso sulla prima! Per me e' 
proprio una bella notizia...
E.

 proprio una bella notizia...
Ottimo. Ma se ci pensi il fatto che l'oggetto venga fisicamente copiato
(con chiamata di costruttore e tutto) oppure venga direttamente creato
in una zona che non viene distrutta quando il frame del chiamante è
distrutto (ovvero metterlo nel frame del chiamato) è un dettaglio
implementativo.
BTW, ci sono diversi modi per evitare il costo e facilitare
l'ottimizzazione.
Quando possibile, ritorna argomenti del costruttore invece che un
oggetto. Cosa intendo?
class Complex;
Complex  operator+(const Complex& lhs, const Complex& rhs) {
        return Complex(lhs.r + rhs.r, lhs.i + rhs.i);
}
....
Complex c = a + b;
Il compilatore può *sicuramente* provare ad ottimizzare un temporaneo
che va fuori esistenza. In questo caso ci sarebbero due temporanei:
quello creato da Complex e quello ritornato. Il compilatore li fa
diventare uno solo. In particolare se tu assegni il tutto ad un valore
fuori, il compilatore crea il tutto direttamente li. E' behaviour
standard e ha pure un nome 'return value optimization. 
L'esempio da te fatto è in qualche modo analogo: il compilatore ha
capito che era la stessa roba che fare return myobject().


 implementativo.
Non direi, quello che ottieni sono due comportamenti diversi. Nel tuo 
esempio la stringa "Copied myobject" non viene mai stampata su schermo!
Credo che in questo caso particolare sia obbligatorio comportarsi in 
questo modo. Ovvero se il valore di ritorno è una variabile locale la 
variabile non viene distrutta, ma ritornata.
Però quello che non capisco è come questo sia possibile quando la 
variabile ritornata non è la prima dichiarata localmente. Ho modificato 
il tuo codice in questo modo:
myobject f() {
   myobject x;
   myobject ob;
   std::cout<<"&x="<<std::hex<<&x<<std::endl;
   return ob;
}
make test && ./test
g++     test.cpp   -o test
Constructed myobject bfa44ad0
Constructed myobject bfa44b20
  &x=0xbfa44ad0
Destroyed myobject bfa44ad0
0
Destroyed myobject bfa44b20
Come si può vedere quando chiamo f() la variabile locale ob viene creata 
sullo stack dopo la variabile x. La variabile x viene distrutta 
all'uscita da f, invece ob rimane sullo stack. Nel main quella variabile 
è giustamente già sullo stack e non viene spostata, in quanto all'uscita 
da main ob viene distrutto e si trova sempre alla stessa posizione. Ma 
allora sullo stack rimane uno spazio vuoto (quello occupato dalla 
variabile x)?!?!? E coma fa la funzione chiamante (main) a sapere dove 
si trova il valore di ritorno di f() ?!?!?
E.

 si trova il valore di ritorno di f() ?!?!?
Conosci il funzionamento del call stack? Faccio un brevissimo riassunto
(se vuoi maggiori ragguagli posso pure darteli). Ovviamente questo
argomento è di per se fuori dal C++ standard, tuttavia dal momento che
la maggior parte delle architetture funzionano così, tutto bene.
Cosa succede quando una funzione chiama un'altra funzione? Viene
allocato un nuovo stack frame sul call stack. Indicativamente questo
frame contiene (in ordine di per se variabile):
1. i parametri attuali della procedura chiamata
2. il valore di ritorno della procedura chiamata
3. l'indirizzo di ritorno
4. le variabili locali della chiamata.
La costruzione dello stack frame avviene in due fasi: una è fatta dal
chiamante, una dal chiamato (nota come prologo). Quello che esattamente
viene fatto in ciascuna delle due fasi, dipende dall'architettura
specifica. 
Ad ogni modo in generale il chiamante alloca spazio sullo stack per i
parametri e per il valore di ritorno (ecco come fa a sapere dove si
trova il valore di ritorno: è lui stesso a dargli l'indirizzo). 
Poi la macchina salta all'indirizzo del chiamato. L'indirizzo di ritorno
viene messo da qualche parte (è tipicamente l'indirizzo della prima
istruzione dopo la chiamata di funzione, quello a cui saltare dopo il
return). 
A questo punto lo stack viene espanso per contenere tutte le variabili
locali, etc etc etc. La procedura locale sa quale deve essere il posto
dove scrivere l'indirizzo di ritorno, in quanto è specificato dall'ABI
come un offset dal frame pointer.
Da tutto questo escludo il fatto che alcune architetture passino
determinati tipi e li ritornino con registri: è un problema secondario.
Se il compilatore si rende conto che un dato oggetto è quello ritornato,
lo costruisce direttamente nello spazio di memoria dedicato al return
address (o fa in altro modo sapere al chiamato dove costruirlo, per
esempio nell'esempio sulla mia architettura il chiamante passa
l'indirizzo di costruzione in un registro e li viene costruito dal
chiamato. Non sono generati distruttori ne nulla, ovviamente. Quando lo
stack frame del chiamato è distrutto non succede nulla all'oggetto,
visto che è contenuto nello stack frame del chiamante) come sopra.
Poi leggere il codice asm generato da un compilatore C++ è come dire...
demanding. Per lo meno per me. 


 visto che è contenuto nello stack frame del chiamante) come sopra.
Questo invece non mi è chiaro. La procedura di chiamata a funzione deve 
essere standard (è quello che tu chiami ABI, vero?) perché la funzione 
chiamata e la funzione chiamante potrebbero essere state compilate 
separatamente, addirittura con due diversi compilatori, giusto?
Dunque la posizione del valore di ritorno deve essere nota alla funzione 
chiamata senza sapere nulla sulla funzione chiamante. Seguendo la tua 
descrizione la funzione chiamata sa qual è l'offset del valore di 
ritorno rispetto allo stack pointer. Questo significa che la funzione 
chiamante non può "comunicare" alla funzione chiamata che vuole avere il 
valore di ritorno da un'altra parte. A meno che sullo stack (o in un 
registro, come tu dici) non venga anche messo un puntatore al valore di 
ritorno... Questo però dovrebbe allora avvenire in maniera sistematica, 
non solo quando c'è la necessità di questa "ottimizzazione"...
E.

 proprio una bella notizia...
In un linguaggio che ha sempre fatto dell'efficienza il suo punto di
forza? Mi sarei stupito piuttosto del contrario.
Invece la liberta' lasciata a chi scrive compilatori e' tantissima: lo
standard specifica la cosiddetta "regola come se", ovvero un programma
C++ corretto deve "solo" comportarsi come se seguisse pedissequamente
le regole stabilite dallo standard. Tutte le ottimizzazioni che non
alterano il comportamento visibile del programma sono ammesse.
Il caso in questione e' un po' diverso perche' i costruttori di copia
possono avere comportamento osservabile ed e' per questo che lo
standard specifica esplicitamente che le copie "inutili" si possono
eliminare.
Ciao,
Nicola

 non solo quando c'è la necessità di questa "ottimizzazione"...
Aspetta: questo *deve* essere fatto in modo sistematico. Ora spiego
anche perchè. Normalmente il valore di ritorno di una funzione è messo
in un registro. Questo è ovviamente fattibile se suddetto valore di
ritorno sta in un registro.
La maggior parte degli oggetti *non* stanno in un registro (come pure le
struct 'a-la-c'). Non voglio ora entrare in dettagli che non conosco a
fondo: l'ABI SysV PPC (e direi pure quella Darwin) dicono esplicitamente
che in questo caso bisogna salvare sullo stack. Non ricordo
*esattamente* i dettagli (tipo se il valore dell'oggetto nello stack
deve essere *anche* passato indietro in un registro, IIRC, si). 
Ad ogni modo l'idea è che se devi ritornare un oggetto 'grande' (dove
grande vuole dire che non sta in un registro) lo devi mettere da qualche
parte in memoria. Non può essere nello stack frame del chiamato perchè
questo è distrutto. Per cui sarà in qualche pezzo dello stack frame del
chiamante. Il quale dovrà passare (in qualche modo) l'informazione al
chiamato.
