Newsgroup: it.comp.lang.c++
Titolo: cin, cout, cerr

> 
http://www.geocities.com/sunsetstrip/stage/7787
 <<
<<<<<<<<<<<< _
Metal...@iol.it
 >>>>>>>>>>>>>>
(remove asterisk to reply)

aggiunta agli stdin, ecc..?
Alternativa.  Servono per non usare piu` stdio.h con tutte le sue magagne.
Alex

 Alternativa.  Servono per non usare piu` stdio.h con tutte le sue magagne.
Quale e' il modo "buono" di fare formatted output con iostream? Per
quanto printf sia una mostruosita' e' dannatamente efficiente per far
uscire fuori roba formattata in modo complesso.
(Io poi odio iostream per l'uso dell'operator<< e simili, secondo me
e' ottimo esempio di perche' NON bisogna fare operator overloading a
meno che l'interpretazione non sia veramente veramente chiara).
Ciao,
Alberto
--
Alberto BARSELLA
PGP fingerprint = 13 3F 22 D2 0B 0A D3 25  F1 89 FE B5 82 AD 75 2A
** Beliefs are dangerous. Beliefs allow the mind to stop functioning.
A non-functioning mind is clinically dead.  Believe in nothing... **

meno che l'interpretazione non sia veramente veramente chiara).
Posizione dogmatica e apparentemente non soggetta a
discussione.  Per me, gli "shift di tot bit" sono petecchie
di minima importanza, l'input/output e` assai piu` diffuso
come esigenza, e << e >> sono la soluzione *ideale* al
difficile problema: come esprimere, senza l'orrore delle
funzioni con numero e tipo variabile (e incontrollabile!) di
argomenti, i concetti di "inserire" oggetti arbitrari in una
classe (ed "estrarre" oggetti da una classe) in un modo
che sia sintatticamente "snello".  Li uso, per analogia al
loro uso in iostream, tutte le volte che devo esprimere
questi concetti astratti, anche quando l'I/O non c'entra,
tipo ad esempio la preparazione di un SAFEARRAY di
VARIANT da passare in una chiamata COM/Automation
(la classe qui incapsula un vector<_variant_t>, l'operator
<<, che e` un template, fa un opportuno push_back, e
quando ho immesso tutti i dati che servono chiamo il
metodo che prepara il SAFEARRAY opportuno).  Meglio
    pack << i << j << 23 << 4.5 << "ciao";
che non
    pack.add(i).add(j).add(23).add(4.5).add("ciao");
almeno secondo me!
Alex

 almeno secondo me!
Io, dopo essermi scritto un po' di tempo fa (per imparare) la classe
"linked list" e avere overloaded gli operatori "perche' cosi' e' piu'
figo" mi sono reso conto che lista.insert(), lista.add_head(),
lista.add_tail(), sono molto piu' leggibili di << >> e simili.  Per
intenredci, nel caso che mostri qui io andrei senza esitazione per la
seconda soluzione.....



mi sono trovato spesso a voler visualizzare il contenuto di una variabile
intera (o comunque diversa da stringa) in una messagebox ma non ho mai
trovato un modo comodo per farlo (tipo la printf()).
Con l'oggetto cout si può fare una cosa del genere in modo veloce?
e poi un'altra domanda: gli oggetti dichiarati in iostream.h non fanno parte
delle MFC, giusto?

Se ben ricordo pero' non funziona cosi'.... :)
Non senza un minimo di aiuto (che non e` pero`
difficile fornire, come vedi dal templatino qui
sopra -- sempre al netto dell'associativita` degli
operatori, naturalmente, che fortunatamente e`
fissa e non si puo` cambiare!-).
Alex

delle MFC, giusto?
Giusto.  iostream.h e` il vecchio header che dichiara gli oggetti della
(ormai obsoleta, ma mantenuta per compatibilita`) "I/O Stream Library";
iostream senza estensione e` il nuovo header che dichiara gli oggetti
della nuova libreria standard del C++ (nel namespace std, ecc ecc).  MFC
e` un'altra e diversa libreria, che si puo` affiancare sia a quella
standard, sia alla vecchia "iostream library", ovvero ad altre ancora.
Alex
--
 ____    Alex Martelli, Bologna, Italia -- email: 
al...@magenta.com
 \SM/___                                 
http://magenta.com/~alex
  \/\bi/     You never know what is enough unless you know what
     \/          is more than enough.

 fissa e non si puo` cambiare!-).
Perche' "fortunatamente"? Io ho sempre trovato un po' strano che sia
possibile ridefinire operatori, ma non aggiungerne e non cambiarne la
"sintassi". Di fatto quando definisci una funzione puoi fare quello
che ti pare (in realta' no, visto che hai sempre un solo return
value), mentre con un operatore non puoi.
"<< bitshift" e' una cosa completamente diversa da "<< write", quindi
non vedo perche' dovrebbero associare nello stesso modo....
Mi sembra principalmente una "eredita'" del C, e una volonta' di non
mettersi a fare troppo casino nel parser del source.


mettersi a fare troppo casino nel parser del source.
Non c'e` nessuna questione di eredita` del C: il C non
permette comunque di ridefinire operatori, quindi Stroustrup
poteva fare quello che voleva per decidere cosa dovesse
significare "ridefinire un operatore", senza per questo che
si perdesse di compatibilita`.  Per il parser del compilatore,
aggiustare al volo le tabelle di precedenza e associativita`
sarebbe banale -- e` il lettore *umano* del codice che non
ha chance in merito.  Non c'e` bisogno di tirare a indovinare,
come tu fai, sulle motivazioni di Stroustrup per le diverse
scelte compiute nella progettazione e nell'evoluzione del
linguaggio: Stroustrup ha scritto un ottimo libro, "The Design
and Evolution of the C++ Programming language", dove
rende ampiamente conto di questo processo storico.
Alex

 ha chance in merito.
Hmmm....questo non mi convince affatto. Esattamente come si distingue
la funzionalita' dell'operatore, distinguendo quindi "<< output" da
"<< bitshift", non vedo perche' la stessa cosa non si possa applicare
anche alla associativita'.
Non metto in dubbio che alterando la sintassi localmente si possano
ottenere delle cose incomprensibili, metto in dubbio che in questo
caso possa succedere cosi' facilmente.


Fra me e Alberto Barsella, in questo thread, si sono spese varie
centinaia di righe dibattendo della sintassi (e semantica) della
formattazione in iostream, in stdio, e in altri schemi.  La mia
tesi, riassumendo e sottolineando i punti di disaccordo, e`: i
mattoni elementari che iostream mette a disposizione sono *proprio
giusti* -- direttamente usabili nel caso tipico, eccellenti per
costruire (con banale semplicita`) strutture alternative quando
servono.
Ora, per dimostrare il punto (B), voglio offrire ad esempio la
realizzazione degli *esatti* desiderata espressi da AB in meno di
cento righe, compresi (modesti) commenti, e con piu` feature di
quante lui ne abbia espresse.  Meno di cento righe, cioe` molte
meno di quante non ne abbiamo spese finora in dibattiti piuttosto
sterili.  Visto che lui sostiene che la situazione e` simmetrica,
cioe` che stdio (e in particolare printf) permettono di costruire
le feature desiderate come lo permettono i potenti iostream, lo
invito a questo punto a replicare scrivendo e mostrandoci tutto il
codice necessario per fare quanto egli asserisce possibile.  E mi
raccomando, eh, AB, per non fare la figura del peracottaro, non
scordarti di includere nel capolavoro in questione la sicurezza
dei tipi a tempo di compilazione, l'estendibilita` in termini di
diverse destinazioni di output, e l'estendibilita` in termini di
nuovi tipi definiti dall'utente.  Dopo di che, riprenderemo
certo volentieri il dibattito.
L'obbiettivo, grosso modo, e` fornire la possibilita` all'utente
di scrivere un programmillo piu` o meno come questo:
-- testab.cpp
#include "abside.hpp"
int main()
{
    abside(std::cout).fmt("======= (===%) ===% ===f ===m").
        write(123456).write(97).write(45).write(106).write(601).
        write("fine\n");
    abside(std::cout).fmt("======= (===%) ===% ===f ===m")
        << 123456 << 97 << 45 << 106 << 601 << " fine\n";
    return 0;
}
-- fine
dove ho mostrato entrambe le forme di zucchero sintattico, del
tutto equivalenti, per l'output di un oggetto -- una funzione
membro "write()", preferita dall'AB, e un piu` snello operator<<,
preferito da me.  La semantica e` d'altra parte identica.  Il
punto-chiave e` il metodo fmt(), che permette di definire il
"look" della stringa di uscita in modo simile alle PICTURE del
Cobol, o del buon vecchio Business Basic (o del Perl, che da
quest'ultimo linguaggio ha tratto a suo tempo vari elementi),
come da specifiche (in questo ragionevolissime) dell'AB.
Il codice che mostro funzionera` con qualsiasi compilatore C++
decentemente allineato allo Standard, come il Visual C++ 5 (o
l'egcs, che e` la versione piu` recente del gcc della FSF).
Il grosso delle funzionalita` delllo schema Abside e` espresso
nel suo header-file:
-- abside.hpp
#ifndef _ABSIDE_H
#define _ABSIDE_H
#include <iostream>
#include <list>
#include <string>
class abside;   // incapsula un ostream ed un formato
class abof;     // rappresenta tutto un formato
class abofel;   // un "elemento" (atomo) di formato
class abofel {   // Un "elemento" (atomo) di formato
    std::string pre; // emettere prima dell'item
    int wid;         // larghezza dell'item
    friend class abside; friend class abof;
public:
    abofel(): wid(0) {} // empty/reset
    abofel(const char* first, const char* last):
        pre(first,last), wid(0) {}
};
class abof {    // Un intero formato di output
    // Comodi typedef e funzioni di forwarding:
    typedef std::list<abofel> container;
    typedef container::iterator iter;
    iter begin() { return format.begin(); }
    iter end() { return format.end(); }
    friend class abside;
    // La sostanza della classe:
    container format;    // sequenza di elementi
    void parse(const char* form, char c);   // parse di formato
public:
    abof(const char* form, char c='=') { parse(form, c); }
    abof(std::string s, char c='=') { parse(s.c_str(), c); }
};
class abside {          // incapsula un ostream+un formato
    std::ostream& o;        // lo stream
    abof* of;               // il formato (NULL->nessuno)
    abof::iter it;          // elemento corrente
    void appfo();           // applica poi avanza l'e.c.
public:
    // ctor e dtor:
    abside(std::ostream&o): o(o), of(0) {}
    ~abside() { appfo(); delete of; }
    abside& format(abof* newof); // prende proprieta` di un formato
    // l'accesso e` normalmente attraverso questi metodi-template:
    template <class T> abside& fmt(T t)		// setta un formato
        { return format(new abof(t)); } 
    template <class T> abside& write(T t)	// emette un item
        { appfo(); o<<t; return *this; }
    template <class T> abside& operator<<(T t)  // idem idem
        { return write(t); }
};
#endif
-- fine
Un piccolo numero di funzioni non sono in linea e quindi vanno
realizzate in un file sorgente a parte:
-- abside.cpp
#include "abside.h"
#include <iomanip>
// costruisce un formato parsando una stringa
void abof::parse(const char *form, char c)
{
    const char *p=form;
    while(*form) {
        while(*p && *p!=c) ++p;
        abofel cur(form,p); form = p;
        while(*p == c) ++p;
        cur.wid = p-form; form = p;
        format.push_back(cur);
    }
    format.push_back(abofel());
}
// applica un elemento di formato
void abside::appfo() 
{ 
    if(!of || it == of->end()) { 
        delete of; of=0; return; 
    }
    o<<(it->pre)<<std::setw(it->wid);
    ++it;
}
// prende proprieta` di un formato
abside& abside::format(abof* newof)
{ 
    delete of; 
    of=newof; 
    it=of->begin(); 
    return *this;
}
-- fine
Lo schema e` un po' piu` ricco e complesso di quanto sinora
richiesto da AB; per esempio, e` possibile costruire una volta
per tutte un oggetto di formato, poi applicarlo quando occorre,
risparmiandosi di ripetere il parsing ogni volta:
    abof riga("======= (===%) ===% ===f ===m");
    while(thereIsMore()) {
	abside(std::cout).fmt(riga)
	    << 123456 << 97 << 45 << 106 << 601
	    << " fine\n";
    }
Mancano invece certe piccole comodita` che probabilmente
andrebbero aggiunte, come la formattazione di numeri floating
point del tipo "=====.==" (trattando anche '.' come carattere
speciale, e usandolo per settare la precision), eccetera.  Ma
il "grosso" (per cosi` dire, perche` questo esercizietto di
C++ non e` certo molto grosso...!-) c'e`.
La libreria standard del C++ e` una collezione di framework,
molto ben progettati, e utili mattoncini funzionali, che gia`
sono direttamente usabili, ma soprattutto sono l'ideale per
costruirsi con minimo sforzo le esatte forme che servono nei
propri programmi.  Chi non lo ha capito, o non accetta questa
impostazione come valida, sta probabilmente usando il linguaggio
sbagliato per il proprio lavoro, e dovrebbe passare ad altri
ottimi linguaggi, meno flessibili e piu` orientati all'immediato
e diretto uso applicativo (come Delphi, Visual Basic, Java,
Eiffel, o altri ancora, a seconda delle sue esatte esigenze),
piuttosto che non sprecare il proprio tempo, ed il nostro,
a lamentarsi di un linguaggio che non fa al caso suo.


Sembrerebbe che vi stiate accalorando un po' troppo.
Ricordate che non e' una guerra di religione e  * soprattutto*
che l'evidenza di una metodologia rispetto ad un'altra non
ne comporta necessariamente l'adozione da entrambe le parti.
Molto spesso, dalle discussioni presenti in questo NG, si ricava
l'evidenza che l'utilizzo di soluzioni specifiche per la risoluzione dei
problemi che di volta in volta si presentano, non sia legata necessariamente
alla qualita' della soluzione, bensi ad una somma di fattori tra i quali
l'abitudine - che porta ad una maggior facilita' di applicazione - ad
applicare determinate soluzioni.
Nel caso in questione riconosco senza ombra di dubbio che le soluzioni
presentate da AM sono eleganti, funzionali e in sostanza formalmente
molto piu' valide. Peraltro non vedo la necessita' di definire le
motivazioni
di AB come vecchie, sorpassate, non funzionali e altro.
In fin dei conti...e' solamente codice per computers.
NOTA: personalmente utilizzo ambedue le "tecniche" senza per questo
sentirmi particolarmente vecchio o particolarmente efficiente.
Massimo

piene.....
Per essere uno che ci tiene cosi` tanto allo zucchero sintattico,
dovresti davvero familiarizzarti con i modi piu` tipici e idiomatici
in C++ di inzuccherare le pillole che il convento passa.  Non che
serva poi piu` di tanto, in realta`, ma, al netto, spenderesti meno
tempo ad implementarti gli inzuccheramenti in questione che ad
autocommiserarti per la loro mancanza.  Non ci vuol proprio niente
a farsene una piccola collezione in un mazzetto di header-file e
oggetti (o librerie, dll, ecc ecc) personali.
Ecco, ad esempio, nell'ipotesi che tu, appoggiandoti sul Regex
della libg++, voglia scrivere programmi (gcc) come questo:
#include <iostream.h>
#include <string>
// dal tuo "headerfile personale", verra` la dichiarazione:
extern void res(string&target, const char* re, const char* rs);
int main()
{
    string myst("A string of mine");
    cout << myst << endl;
    // match delle prime 4 parole (e spazi bianchi fra di esse)
    string words("\\([ ]*\\)\\([^ ]*\\)\\([ ]*\\)\\([^ ]*\\)\\([ ]*\\)\\([^ ]*\\)\\([ ]*\\)\\([^ ]*\\)");
    // scambia la seconda e la quarta parola della stringa
    res(myst, words.c_str(), "$1$2$3$8$5$6$7$4");
    cout << myst << endl;
    return 0;
}
come potresti implementarti la res.cpp:
// Regular-Expression Substitute
#include <Regex.h>
#include <string>
#include <ctype.h>
void res(string&target, const char* re, const char* rs)
{
    Regex Re(re);
    int matchlen;
    int matchstart = Re.search(target.c_str(), target.size(), matchlen);
    if(matchstart<0) return; // nulla da cambiare se non trovato
    const char* p = rs;
    string subi;
    while(*p) {
	if(*p=='$') {           // il $ carattere speciale...
	    char c = p[1];
	    if(isdigit(c)) {	// ...se e solo se seguito da cifra
		int start, len;
		int rc = Re.match_info(start, len, c-'0');
		subi += string(target, start, len);
		p += 2;
	    } else {		// $ non speciale (salvo $$)
		subi += *p++;
		if(c=='$') ++p;	// $$ -> inserisce un $
	    }
	} else {		// caratteri normali
	    subi += *p++;
	}
    }
    target.replace(matchstart, matchlen, subi);
}
Il C++ e` un insieme di mattoni, calce, travi, ecc.  L'ideale per
costruirsi esattamente la casetta (o il grattacielo) che uno
preferisce.  Chi vuole i prefabbricati, si trovera` molto meglio
in altri ottimi linguaggi, tipo Java, Smalltalk, Visual Basic...


sentirmi particolarmente vecchio o particolarmente efficiente.
perfettamente d'accordo, anch'io uso entrambi i sistemi a seconda del
tipo di lavoro che sto facendo; comunque preferisco quando possibile
cercare di evitare il vecchio stdio.
Gianpaolo Bottin
- Live Long and Prosper -

propri programmi.
in linea di massima sono d'accordo con te anche se poi in pratica sono
un discreto utilizzatore di sprintf() e compagnia :(, il fatto e' che
spesso mancano sia il tempo e la voglia di scriversi un sistema di
output formattato che soddisfi le proprie esigenze e cosi' alla fine
per inerzia si continuano ad usare i vecchi sistemi. Sarebbe ben
diverso se avessi a disposizione (magari gia' nella libreria standard)
un sistema di output formattato in grado di replicare piu' o meno le
potenzialita' della printf(). Da questo punto di vista ho gia' tirato
un po' di maledizioni a MS perche' hanno perso del tempo a scrivere la
CString::Format andando anche a calcolare le dimensioni della stringa
risultante in modo da poter preallocare la memoria necessaria... ma
non era piu' semplice introdurre un nuovo sistema di output formattato
con stile con il C++ ? Mah !


 certo volentieri il dibattito.
Parto per una conferenza domani e torno lunedi', quindi aspetterai una
settimana prima di avere la risposta......
Solo un commento: ancora una volta l'unica cosa che hai fatto e'
dimostrare che PUOI, usando il C++ iostream etc., farti quello che
vuoi e che questo rappresenta la superiorita' del C++.
Se dovessi mandarti la risposta scritta in C o magari in assembler
(automaticamente non portabile, ovvio), questo significherebbe che
"sono pari" al C++?
Se mi scrivo una implementazione O-O in Fortran (ci ho ripensato e
ne' l'allocazione dinamica ne' le structures servono), secondo te
il fortran diventa adatto all'O-O?
La domanda iniziale era: "perche' iostream ha una formattazione cosi'
scomoda (per me)?" Non "cosa si puo' fare con iostream?"
Per quanto ti possa piacere il C++ evita, per favore, la difesa a
livello religioso (cazzo, prendi l'esempio che mi hai fatto con le
regexp e riscrivilo in perl 5, sono 3 dico *3* righe, come fai a dire
che in C++ non e' piu' pesante! Con questa logica non e' piu' pesante
neanche l'assembler riscrivendosi sia il supporto per le stringhe che
quello delle regexp da zero, ci vogliono solo piu' linee di codice!).


quello delle regexp da zero, ci vogliono solo piu' linee di codice!).
"Il quantitativo e` qualitativo".  Venti o cento righe da piazzare nella
propria libreria personale sono una cosa, mille o cinquemila ben
altra -- o le troviamo gia` fatte (e adattabili ai nostri scopi con il
modesto sforzo delle 20-100 righe circa di "rivestimento"), o se
no si tratta di un progetto molto oneroso.  Queste considerazioni
*dovrebbero* essere ovvie, ma chiaramente non a tutti e` ovvio
quel che dovrebbe esserlo (e qui, per capire il perche` ed il
percome, torniamo al discorso dello psicologo...:-).
Alex

 percome, torniamo al discorso dello psicologo...:-).
Qui hai speso piu' neuroni a trovare il modo di prendermi in giro che
non a pensare alla risposta. Ti faccio notare che il mio atteggiamento
iniziale (uso la printf, abbasso iostream!) e' proprio il risultato di
questo ragionamento. "La printf fa quello che voglio e fa abbastanza
per le mie necessita => la uso".  Sei te che nel tuo slancio pro-C++
ti sei messo a dire che iostream e' molto meglio e hai citato un
fracco di potenzialita' che a me non servono.  Sei te che vuoi
rifilarmi la Ferrari quando mi basta la Panda...(oddio, se mi regali
una Ferrari io accetto....  BTW gia' mi immagino una risposta che
comincia con "class Ferrari {....}" - tutto private, poi, per evitare
che se la freghino :-))
