Newsgroup: it.comp.lang.c++
Titolo: eccezioni e puntatori

ciao, sto facendo un po di esempi con il C++ e stavo provando a creare
una mia classe per gestire eccezioni, ecco il codice:
MyException.h
#include <string>
using namespace std;
class MyException{
private:
	const string* erBadIO;
public:
	MyException();
	virtual ~MyException();
	string getErBadIO();
	void showErBadIO();
	void setErBadIO(const string* msg);
};
MyException.cpp
#include "MyException.h"
#include <string>
#include <iostream>
using namespace std;
MyException::MyException(){
	string bad;
	bad = "err";
	this->erBadIO = &bad;
}
MyException::~MyException(){}
void MyException::setErBadIO(const string* msg){
	this->erBadIO = msg;
}
void MyException::showErBadIO(){
	cout << this->getErBadIO() << endl;
}
string MyException::getErBadIO(){
	return this->erBadIO; //QUI mi da errore in quanto ritono un
puntatore e non so come dichiarare la funzione
}
penso che non sia un gran esempio da applicare nella vita reale, ma
stavo provando solo per vedere, se vedete delle "butte cose"
segnalatemelo! grazie!

 una mia classe per gestire eccezioni, ecco il codice:
[...]
Il problema con la classe MyException è che contiene un puntatore a
std::string e non lo gestisce.
Nel costruttore viene assegnato a erBadIO l'indirizzo di una stringa
che non esisterà più quando il costruttore avrà terminato.
Un oggetto di classe MyException parte già male; ha un puntatore a
stringa che punta ad una zona di memoria che non è più valida.
Il distruttore ignora completamente il problema della gestione della
memoria a cui punta erBadIO.
La funzione getErBadIO dovrebbe ritornare un puntatore a stringa, se
desideri eliminare l'errore.
Dovrebbe essere: string* MyException::getErBadIO()
Per ora dovresti avere abbastanza informazioni per poterci lavorare un
po' su.
Comunque sarebbe meglio memorizzare il messaggio di errore "by value",
così eviti i problemi di gestione dei puntatori e della memoria
allocata dinamicamente.
Ti consiglio di guardare come sono dichiarate le eccezioni standard
nei file <stdexcept> e <exception>, per prendere spunto.
Ciao, Ugo

 segnalatemelo! grazie!
Parecchie, in effetti. Suggerirei un buon libro su C++. Mi da idea che non
hai punto chiara la semantica di molti costrutti fondamentali.
-- 
  Enrico Franchi (RiK0)
[ Yellow Dog Linux User ]
[ #299188 Linux Counter ]
[ MacOS X User          ]

 limite* (ma sui due piedi non ne vedo il vantaggio)  uno shared_pointer.
A quanto ne so la std::string è già di suo implementata con smart 
pointers... è fatta apposta per essere passata per valore.
E.

 pointers... è fatta apposta per essere passata per valore.
È completamente verosimile che lo sia. Tipo sulla mia piattaforma sono
precisamente 4 byte.
Non credo che sia *necessario* che lo sia (anche se appunto non riesco a
pensare a implementazioni ragionevoli di string che non lo siano).
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

 precisamente 4 byte.
Quella del GCC, ma credo anche molte altre implementazioni, non usano 
smart pointers ma buffer con reference count.
In pratica se fai:
int pippo(const std::string &a)
{
	std::string b = a;
}
b contiene lo stesso buffer di a con il reference count incrementato di 
1, se pero' si prova a scrivere su b il buffer viene copiato ed il 
reference count diminuito.
I counter sono gestiti tramite delle chiamate atomiche che purtroppo non 
vengono esportate perche` sarebbero molto comode in ambiente cross 
platform (win32/mac/linux, tutti con GCC).
MMh facendo una verifica ho visto che forse includendo bits/atomicity.h 
e usando il namespace __gnu_cxx si possono riuscire ad usare :)
--
Bye,
  Gabry

reference count diminuito.
Sì, la tecnica del copy-on-write viene usata anche da molti altri
sistemi di sviluppo (vedi Delphi).

 smart pointers ma buffer con reference count.
Interessante. Io ero su OS X in effetti (quindi gcc).
Dal che deduco che intendi che il reference counting lo tengono *dentro* il
buffer (quello che intendi con 'buffer con ref count', giusto?)


 pensare a implementazioni ragionevoli di string che non lo siano).
Uhm. Per quanto ne so il trend e' per l'eliminazione del reference
counting/copy on write, perche' nelle applicazioni multi-threaded la
necessita' di manipolare atomicamente il contatore tende a cancellare
i benefici. Il problema e' particolarmente grave a causa della
possibilita' di acquisire accessi modificabili alla stringa, via
iteratori e indicizzazione.
In questo hanno probabilmente ragione quelli di Java a distinguere tra
stringhe immutabili e stringhe modificabili.
Ciao,
Nicola

 stringhe immutabili e stringhe modificabili.
D'altra parte le stringhe immutabili in C++ sarebbero un bel PITA. IMHO,
eh. Cioè sono una iper-faq di Python con tanto di anti-pattern, mi
chiedo il macello che sarebbe in C++, tenendo conto del resto del
linguaggio.
In particolare si potrebbero usare gli stringbuffer come in Python si
usano gli StringIO, ma il metodo tipico, quello di concatenare le
stringhe mediante join di liste sarebbe brutto.
Ovviamente sarebbe completamente contro la filosofia di C++ quello di
dare a tutti i contenitori un metodo 'join' (specie perchè sarebbe
templaticamente specializzabile solo per pochi tipi, certo, si può dare
un'implementazione di default 'vuota', ma non vedo spesso le cose fatte
in quel modo nella STL).
Va da se anche metterlo come metodo di std::string farebbe schifo. Già
mi fa schifo in Python, credo che convincere un programmatore C++ a
scrivere:
std::string("").join(v.begin(), v.end())
sia sadismo.
Immagino ci si standardizzerebbe su una funzione join definita in
<string> del tipo
template <typename Iterator>
std::string join(Iterator begin, Iterator end) {
    typedef typename std::iterator_traits<Iterator>::value_type val;
    mutable_string void_("");
    return std::accumulate(begin, end, void_);
}
Da cui...
std::join(v.begin(), v.end());
Beh, li ho usato accumulate, immagino ci andrà un algoritmo più furbo
dentro.


 stringhe mediante join di liste sarebbe brutto.
Non sono sicuro di seguirti. L'immutabilita' comincerebbe dopo la
costruzione, per cui nulla vieterebbe qualcosa come
std::fixed_string fs(v.begin(), v.end());
Cosi' come non vedo grossi problemi a gestire
std::fixed_string fs1 = fs + " abc" + std::string("def");
Il punto fondamentale e' che dopo la creazione la stringa sarebbe
modificabile solo tramite assegnamento e si comporterebbe in maniera
piu' amichevole rispetto al reference counting. Secondo me i casi in
cui non serve altro sono una grossa parte del totale.
Ciao,
Nicola

 cui non serve altro sono una grossa parte del totale.
Come dicevo, ho una certa esperienza nel manipolare stringhe in Python.
Suddette stringhe sono per l'appunto immutabili.
Un pattern *frequentissimo* è quando si vogliono sommare tante piccole
stringhe. Se hai le stringhe immutabili, ogni volta che hai un 
s1 + s2
viene costruita una nuova stringa. Il che vuole dire che con n somme (il
che accade, non riesco a farti un esempio sui due piedi, ma è piuttosto
frequente) hai il costo di n costruzioni ed n distruzioni (e in C++ le
distruzioni sono fatte *davvero*).
Per cui è molto meno costoso fare la faccenda se hai una funzione come
la join di cui sopra. Certo, le fixed string avrebbero una roba del
genere nel costruttore, verosimile, e poi si potrebbe implementare join
con in termini del costruttore, vero. cambia poco.


 cui non serve altro sono una grossa parte del totale.
Ho una certa esperienza nel manipolare stringhe in Python.
Suddette stringhe sono per l'appunto immutabili.
Un pattern *frequentissimo* è quando si vogliono sommare tante piccole
stringhe. Se hai le stringhe immutabili, ogni volta che hai un 
s1 + s2
viene costruita una nuova stringa. Il che vuole dire che con n somme (il
che accade, non riesco a farti un esempio sui due piedi, ma è piuttosto
frequente) hai il costo di n costruzioni ed n distruzioni (e in C++ le
distruzioni sono fatte *davvero*).
Per cui è molto meno costoso fare la faccenda se hai una funzione come
la join di cui sopra. Certo, le fixed string avrebbero una roba del
genere nel costruttore, verosimile, e poi si potrebbe implementare join
con in termini del costruttore, vero. cambia poco.
-- 


 con in termini del costruttore, vero. cambia poco.
Volendo esagerare il modo giusto di implementare l'operatore + per le
stringhe in C++ è tramite expression templates, in modo da limitarsi a
tenere un puntatore alle varie stringhe per scorrerle solo al momento
in cui si costruisce effettivamente la stringa destinazione.
Ciao,
Nicola

 
...
Nel framework .NET ci sono 2 concetti:
String: le stringhe immutabili (che sono le più usate e le + performanti)
StringBuilder: che sono mutabili e servono per costruire stringhe "a 
pezzi". Oltre al resto c'è un metodo Append che è comodo:
s.Append("ciao ").Append("a").Append(" tutti") ...
(In pratica molto simile alla sstream)
Quando hai finito di "comporre" la tua stringa ne ricavi una immutabile.
Non mi farebbe schifo questa distinzione.

 in cui si costruisce effettivamente la stringa destinazione.
Hai un esempio? Sui due piedi non ho capito di preciso quello che
intendi.


 Non mi farebbe schifo questa distinzione.
Diciamo che in Java è uguale sputato, in Python pure.
In Java lo trovo abbastanza scomodino, in Python (che è già più comodo
di suo) no. Ma temo proprio che in C++ potrebbe essere meglio che in
Java, ma non altrettanto comodo che in Python. Ma potrei non avere visto
le interfacce probabili con la lucidità di Nicola.


 intendi.
Credo che Nicola abbia in mente qualcosa del genere:
  template< class A, class B >
  struct appendable_pair
  {
        const A& a_ref;
        const B& b_ref;
 
        appendable_pair( const A& ar, const B& br ) : 
              a_ref(ar) , b_ref(br) { }
 
        operator std::string() const
        {       std::string s;
                return append(s,*this);
        }
  };
 
  template< class S, class A, class B >
  std::string& append( S& s, const appendable_pair<A,B>& pair )
  {
        return append( append( s, pair.a_ref ), pair.b_ref );
  }
 
  std::string& append(  std::string& s, const std::string& that )
  {     s += that; return s;
  }
 
  // al posto del "+" ho usato l'operatore "/"
 
  template< class A, class B >
  appendable_pair<A,B>
  operator/( const A& a, const B& b )
  {     return appendable_pair<A,B>(a,b);       
  }
 
 
  int main()
  {
        std::string a = "hello ";
        std::string b = "world";
 
        std::string result ;
 
        result =  a / b / "!" / "!"  ; // ecco!
 
        cerr << result << endl;
 
  }
Comunque, anche se so che alcuni inorrideranno, nei miei programmi per
concatenare stringhe mi avvalgo di un apposito overload dell'operatore <<.
Questo mi consente di scrivere:
 std::string result;
 result << a << b << c << d;
Che è leggibile ed efficiente.
Max

 Che è leggibile ed efficiente.
Beh, in fondo e' una specie di ostringstream lightweight, no? :-)
Ciao,
Nicola
P.S. Ho ricevuto i saluti, grazie :-)

 Credo che Nicola abbia in mente qualcosa del genere:
Capito. Bello.
