Newsgroup: it.comp.lang.c++
Titolo: Vorrei fare una classe che sostituisse cout

Avevo gia postato questo problema un po' di tempo fa e quindi ne approfitto
per ringraziare tutti coloro che mi hanno aiutato. Purtroppo pero`,
nonostante i miei sforzi non ho ancora risolto il problema. Magari sono
stato poco chiaro nell'esposizione allora mi spiego meglio:
Sto facendo un programma che e` praticamente finito. L'unica funzione che
manca e` quella di mettere un parametro in base al quale aumentare/diminuire
il livello di verbosita` in output.
In questo programma si incontrano spesso delle istruzioni del tipo:
cout << qualcosa;
dove "qualcosa" sta per stringhe, numeri, puntatori, classi definite da me.
Insomma, stampo un po' di tutto.
A seconda del livello di verbosita` che l'utente seleziona, alcune o tutte
queste scritte vanno eliminate. Allora ho due possibilita`:
1) Prima di ogni cout metto un if del tipo:
if (condizione)  cout << qualcosa;
2) Mi faccio io una classe apposta e sostituisco:
cout << qualcosa;
con
miaclasse << qualcosa;
Poi sara` miaclasse che andra` a vedere se stampare o meno (richiamando la
cout) in base alla condizione.
Chiaramente la seconda soluzione mi sembra la migliore nonche` l'unica
seriamente proponibile. Il fatto e` che non ho la minima idea di come
realizzare una siffatta classe. Se e` facile (oltre che ben documentato su
tutti i testi che ho letto) fare il contrario (cioe`: cout << miaclasse)
quello che voglio fare io mi risulta difficile. In giro non ho trovato
niente che mi aiutasse.
Se c'e` qualcuno che si e` gia cimentato con un problema simile o che ha
un'idea da darmi per impostare il problema gliene sarei molto grato.
Ciao
Roberto.

 cout) in base alla condizione.
Non potresti usare uno std::ostringstream, ridefinendo nelle classi che hai 
creato l'operator<< che lo prenda come argomento? Anche volendo fare una 
classe tua dovresti comunque ridefinire in ogni altra classe l'operator<<, 
per cui un ostringstream mi sembra la soluzione migliore.
Ciao,
Cecco

 miaclasse << qualcosa;
[CUT]
Devi ridefinire l'operatore << per ogni tipo che userai, e potresti
istanziarla passando al costruttore il tuo livello di "verbosita'"
cosi' da controllarne l'output localmente in ogni funzione...
L'esempio che ti allego dovrebbe andare bene ai tuoi scopi, pero'
attento:
se dici visualizzami gli interi ma non le stringhe e scrivi cosi':
       miaclasse <<"il numero e'"<<10;
la stringa tra doppi apici viene ignorata, e magari il commentino
poteva essere utile.
                 Ciao Stefano
class mystream
{
public:
	mystream(){}
	mystream(int level):m_verbose_level(level){}
	~mystream(){}
	mystream& operator<<(int x)
	{
                if(mia condizione)
		  std::cout<<x;
		return *this;
	}
	mystream& operator<<(const char* x)
	{
                if(mia condizione)
		  std::cout<<x;
		return *this;
	}
private:
	int m_verbose_level;
};
int main(int argc, char* argv[])
{
	mystream s;
	s<<1<<6<<9<<"\n";
 
	return 0;
}

 }
Mi sembra improponibile. Cioe`, posso anche ridefinire l'operatore per ogni
tipo ma credo che non sia il modo corretto. Va bene per interi e stringe ma
io posso voler stampare anche classi sconosciute. Ěnsomma, quando io scrivo:
myclass m;
cout << m;
non e` che la cout ha definito un operator<< per tutto, ma va a richiamare
l'operator<< che IO ho definito dentro la classe myclass. Vorrei fare una
cosa simile anche io.
Tieni presente che, una volta fatta la classe, questa deve poter servire a
gestire i log anche di altri programmi fatti da me o da chiunque altro.
Ti ringrazio comunque per l'aiuto, tutto fa brodo. Sono sicuro che un po'
alla volta arrivero` alla soluzione del problema. Se hai altri suggerimenti
non farti scrupoli.
Ciao
Roby

 Cecco
Preferirei di no, ti spiego perche`: le classi che ho definito io sono
abbastanza semplici ed e` possibile (ed auspicabile) che vengano utilizzate
in altri programmi. Ora, quando uno manda in stampa un oggetto, si aspetta
che questo venga stampato e non che prima di mandarlo in stampa sia
necessario verificare che il livello di verbosita` sia corerente con le mie
aspettative. Sarebbe come se io dovessi stampare una stringa e fossi
costretto a scrivere qualcosa del genere:
string s;
cout.set_verbosity_level(PRINT_ALL);
cout << s;
Cioe` la verbosita` non deve essere qualcosa di intrinseco al programma, ma
una libreria esterna da importare ed utilizzare solo nelle stampe finali,
quelle che vengono richiamate dagli oggetti di livello piu` alto, che sono
strettamante legati al contesto del programma.
Ti ringrazio comunque per i suggerimenti, sono sempre bene accetti.
Ciao
  Roberto.

 
Per ottenere cio' e' necessario che la classe di chiunque altro
definisca
l'operatore <<, altrimenti saresti costretto ad impicciarti dei
dettagli di
implementazione di ogni classe.
Ciao Stefano

 miaclasse << qualcosa;
non credo che sia la migliore idea; fossi in te proverei con qualcosa 
del genere (non compilato):
class Logger {
public:
        enum Level { fatal_error = 0, critical_error, warning, info, debug };
        Logger(Level v = warning, std::ostream& o = std::cout):
                verbosity(v), os(o)
        {
        }
        template<class T>
        void Print(const T& x, Level verb)
        {
                if (verb <= verbosity)
                        os << x;
        }
 
private:
        Level verbosity;
        std::ostream& os;
};
 
ciao Anto
-- 
Antonio Cuni, 19 anni, studente
Brainbench C++ Master: 4.73, higher than 99% of all previous test takers.
C Language: 3.79, higher than 92% of all previous test takers.
Guida C/C++ di Programmazione.it: 
http://cpp.programmazione.it

         }
*snip*
Ti ringrazio molto. Era proprio questo che mi sfuggiva. In effetti avevo
pensato anch'io ad un metodo print in una classe logger come soluzione
alternativa ma non sapevo come fare quello che tu hai fatto con il template.
Dopo che ho visto il tuo codice mi sono subito riletto il capitolo sui
template ed ora sono riuscito a fare una cosa con il livello di eleganza,
chiarezza e semplicita` che stavo cercando.
Ciao
  Roberto.

 *snip*
potrebbe essere quella di derivare la tua classe Logger da streambuf
(soluzione che ritengo migliore, ma che non ho ancora implementato), o che
gestisca un oggetto ofstream. Questo ti evita di dover definire un metodo
"print", e di limitare l'overload dell'operatore <<. Oltre a poter gestire
il tuo output su file nel modo che ritieni migliore.
class Logger
{
    private:
        ofstream fileStream;
    public:
       friend ostream& operator << (Logger  &obj, const string &str) {
return obj.fileStream << str; }
       friend ostream& operator << (ostream &os, const Logger  &obj) {
return os << obj.fileStream; }
}
A questo punto, le classi che devono produrre un log, è sufficiente che
abbiano un loro overload dell'operatore ostream.
class miaClasse
{
    ...
    public:
       friend ostream &operator<<(ostream &out, const miaClasse &obj);}
Logger  gestoreLog;
gestoreLog << "pippo";
gestoreLog << miaClasse;
Ciao
Maurizio.

 poter gestire il tuo output su file nel modo che ritieni migliore.
[cut]
non credo la tua sia la soluzione migliore, dato che mi pare fragile.
Mi spiego: un'operazione di logging "selettivo" richiede intrinsecamente 
tre parametri:
1) il logger su cui stampare
2) il messaggio da stampare
3) il livello di attenzione di tale messaggio
Non esiste un modo semplice e privo di difetti per passare questi tre 
parametri a operator<<, dato che esso ne accetta solo due.
Una funzione di appoggio come puo` essere la mia Print risolve 
tranquillamente questo problema, anche se si deve rinunciare all'usuale 
interfaccia degli stream.
A dire il vero esiste una soluzione che sembrerebbe salvare capra e 
cavoli, ma ha anch'essa qualche difetto; l'idea di fondo e` di 
utilizzare un meccanismo simile ai manipolatori degli stream standard 
per impostare il livello di attenzione del prossimo messaggio:
class Logger {
public:
   enum Level { fatal_error = 0, critical_error, warning, info, debug };
   // imposta il livello di attenzione
   friend Logger& operator<<(Logger& log, Level lev);
...
};
Da usarsi in un modo simile a questo:
        Logger log;
        log << Logger::warning << "Warning: ... \n";
Pero` in questo caso occorre risolvere un dilemma: quanto "deve durare" 
il livello di attenzione impostato dai manipolatori? Principalmente 
abbiamo due opzioni:
1) solo per il successivo messaggio
2) per tutti i messaggi successivi sino a nuova indicazione.
La soluzione 1 ha l'evidente difetto di non consentire codice di questo 
tipo:
        log << Logger::critical_error << "Errore numero " << err_num;
dato che dopo aver stampato "Errore numero " il manipolatore avrebbe 
perso il suo effetto; occorrerebbe scrivere
        log << Logger::critical_error << "Errore numero " << 
Logger::critical_error << err_num;
che e` decisamente troppo prolisso.
D'altra parte anche l'opzione 2 ha i suoi difetti: il principale e` che 
disaccoppia il messaggio dal suo livello di attenzione, rischiando di 
trovarci con codice del genere:
        log << Logger::warning;
        log << "xxx"; // livello warning
        ...
        log << "yyy"; // ancora livello warning
Se in fase di manutenzione ci accorgiamo che "xxx" in realta` e` un 
critical_error e cambiamo solo la prima linea di codice abbiamo un 
comportamento errato nell'ultima. In realta` per evitare questo errore 
basterebbe un po' di disciplina, ma il solo fatto che e` possibile rende 
il codice potenzialmente buggato.
ciao Anto
-- 
Antonio Cuni, 20 anni, studente


 interfaccia degli stream.
*snip*
Ho provato questa soluzione ma ricevo un errore dal linker che non mi
spiego.
Uso Visual C++ 6 ma l'errore l'ho avuto anche sotto linux con gcc quindi
tenderei ad escludere peculiarita` del compilatore specifico.
L'errore e` del tipo: "unresolved external symbol..." che e` anche riportato
nell'msdn:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HT
ML/lnk2001.asp
Sembra che si presenti solo quando creo la classe logger in un file
separato.
Per maggiore charezza ho creato una versione "scheletrica" del programma in
cui ho lasciato le parti critiche.
------------------ logger.h
class logger{
public:
  logger();
  ~logger();
  template <class T>
    void stampa(const T& msg) const;
};
------------------ logger.cpp
#include "logger.h"
logger::logger(){}
logger::~logger(){}
template <class T>
void logger::stampa(const T& msg) const {
  cout << msg;
}
------------------ main.cpp
#include "logger.h"
int main(){
  logger L;
  L.stampa("ciao");
  return 0;
}
Se compilo il codice qui sopra totto ok... fino a quando fa il link.
Hai idea di come fare sparire l'errore?
Ciao
Roby

 riportato nell'msdn:
 
probabilmente l'errore e` dovuto al member template logger::stampa(). 
Per poterlo istanziare il compilatore deve avere sott'occhio il body 
della funzione template, per cui dovresti scriverlo all'interno 
dell'header.


 dell'header.
*snip*
E` vero, hai ragione. Fatto in quel modo funziona tutto. Chissa` io quando
ci sarei arrivato... :)
Grazie.
Roberto.
P.S.: Guardando la signature agiungerei anche... buon compleanno!

 wrote in message
news:ac5594$c34$2...@newsreader.mailgate.org...


 e il livello di log veniva modificato fino all'endl
[cut]
soluzione interessante... ma mi pare un po' rischiosa: e se 
l'utente si dimentica di mettere l'endl e si limita ad un new-line? Il 
livello di log rimane l'ultimo impostato anziche` quello di default...
