Newsgroup: it.comp.lang.c++
Titolo: Ancora 'stile'

Assumete di avere
S s;
if (!f(s))
{
   a();
   b();
   return false;
}
if (!g(s))
{
   a();
   b();
   return false;
}
if (!h(s))
{
   a();
   b();
   return false;
}
Come risolvete?
La seguente forma è abbastanza comprensibile?
S s;
if (!f(s)
  || !g(s)
  || !h(s))
{
   a();
   b();
   return false;
}
Kiuhnm

 }
Secondo me si.
Ciao,
Nicola

 Secondo me si.
Confermo, anche secondo me.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 }
Sì, è comprensibile (magari se riesci a far stare tutta la clausola 
dell'IF su una riga sola è meglio). Io per abitudine quel tipo di if li 
scrivo sempre completi, vale a dire:
  if (!f(s)
   || !g(s)
   || !h(s))
  {
    a();
    b();
    return false;
  }
  else
  {
    return true;
  }
perchè in questo modo il codice non presuppone che la variabile 
"ricevente" valga inizialmente true: modifiche successive, fatte di 
fretta e senza ricordarsi una mazza di quanto scritto in origine, 
potrebbero invalidare questo assunto e creare un bel bug.
Con l'IF completo invece si assegna in ogni caso il valore corretto.

 {
Proporrei:
#define unless(X) if(!(X))
 unless ( f(s) && g(s) && h(s) ) {
(Scherzo.)
Max

 Con l'IF completo invece si assegna in ogni caso il valore corretto.
Non ti ho capito del tutto...
Kiuhnm

 (Scherzo.)
De Morgan + Kiuhnm?
Kiuhnm

 
Anche per me e' comprensibile.
Anche se personalmente preferirei:
if(!f(s) || !g(s) || !h(s))
(a meno che la riga non sia troppo lunga...)
-- 
Roal Zanazzi

Confermo, anche secondo me.
E' leggibile però IMHO in certi casi è poco intuitivo.
Generalmente io "penso positivo" :) nel senso che, data una condizione
che apre la strada a una certa elaborazione, se devo implementare il
caso in cui questa condizione non sia verificata, allora la sottopongo
al controllo negativo nel complesso.  Cioè, se ho:
if ((c'è il sole) && 
    (nel bosco non c'è il lupo) && 
    (il cestino è pieno) {
 
   vai_dalla_nonna();
}
allora, nel caso testi il fallimento della condizione, mi è più
intuitivo scrivere:
if (!((c'è il sole) && 
      (nel bosco non c'è il lupo) && 
      (il cestino è pieno))) { ...
che non:
if (!(c'è il sole) ||
    !(nel bosco non c'è il lupo) || 
    !(il cestino è pieno)) { ...
Alla fine comunque credo siano questioni di gusti...

 {
io uso la forma indentata multiriga solo per scrivere il codice 
e contare di avere annidato correttamente, indi cancello tutti i 
return qualsiasi lunghezza abbia la riga.
Nel senso che trovo (personalmente) prioritaria l'esigenza della 
leggibilità del codice a medio raggio (paginata/e) che non 
localmente alle singole righe (su cui tendo a considerarle sulla 
fiducia e cmq mi crea meno problemi a capire lo scroll 
orizzontale). Il codice molto diluito su più righe che rende una 
funzione lunga molte pagine da "scrollare" mi affatica la 
memoria buffer.
D'altra parte so per certo che molti detestano il modo mio, 
molto denso, con pagine piene, di scrivere funzioni corte come 
numero di righe.
E' possibile che sia un condizionamento acquisito dal non avere 
mai avuto monitor più grandi di 14 pollici, non ridete, nei 
quali il numero di righe per pagina è una risorsa preziosa.
Ah, cosa stupida, il not anteposto non mi piace molto, e tendo a 
tradurlo nella forma (f(s) == 0) completamente parentetizzata.
Sono manie personali. PErsino il true sottinteso tendo a 
esplicitarlo con (f(s) != 0).
ciao
Soviet


 con (f(s) != 0).
Da qualche parte devo avere un "!!ret". Evidentemente non avevo voglia 
di scrivere "ret != 0".
Kiuhnm

tradurlo nella forma (f(s) == 0) completamente parentetizzata.
Eh già, però strano a dirsi è bizzarra pure questa forma, perché
equivale a:
((f(s)==0)!=0)
e questa a:
(((f(s)==0)!=0)!=0)
e questa a:
((((f(s)==0)!=0)!=0)!=0)
e questa a:
....
:-P

 Non ti ho capito del tutto...
Già... il fatto di avere un return nasconde il punto della questione. 
Supponi di avere invece un codice di questo tipo:
if (!f(s)
  || !g(s)
  || !h(s))
{
   a();
   b();
   x = false;
}
per come è strutturato, questo IF presuppone che inizialmente x = true, 
e se per caso questo non è vero il programma ha un bug.
Invece l'IF completo come sono abituato a farlo io non presuppone niente 
e assegna in ogni caso ad x il valore corretto. Probabilmente è solo una 
fisima che ho io, tanto basta un minimo di attenzione per non introdurre 
questo tipo di errori con l'if, però a me piace di più così...

 questo tipo di errori con l'if, perň a me piace di piů cosě...
Una volta scrivevo
bool r = false;
if (f())
   r = true;
invece adesso scrivo sempre
bool r;
if (f())
   r = true;
else
   r = false;
E' solo un esempio. E' chiaro che in questo caso scriverei
bool r = f();
Perň come trasformo il seguente?
unsigned r;
if (f())
   r = 20;
else
   r = 30;
Forse in
r = f() ? 20 : 30
oppure nell'illeggibile (e, in qualche modo, meno portabile)
r = 30 - (((unsigned)f()-1) & 10);
No, meglio il precedente...
Ma sto divagando!
Kiuhnm

 r = 30 - (((unsigned)f()-1) & 10);
r = 20 + (((unsigned)f()-1) & 10);
Kiuhnm
P.S. Come volevasi dimostrare...

 pari, con varie combinazioni di != e ==.
A dire il vero, c'è una bella differenza tra il primo e i successivi. Il 
primo (se necessario) esegue una conversione esplicita a bool, mentre 
gli altri no, quindi sono superflui.
In c# mi pare sia stata eliminata la conversione implicita a bool. 
Probabilmente molti bug erano provocati da essa.
Per es. non è più possibile compilare
int i = 0;
if (i = 1)
   cout << "M$ sucks!" << endl;
e, anche solo per questo motivo, hanno deciso di eliminarla.
Kiuhnm

 r = 30 - (((unsigned)f()-1) & 10);
Non e'
r = 30 - ( f() * 10 );
Ma userei anch'io l'operatore ternario.
Ciao,
Nicola

 }
In realta' in presenza di condizioni complesse spesso e' il contesto a
influenzare l'organizzazione del codice. Un esempio tratto da codice di
produzione:
bool P::matchesFilter(C & c) const
{
    return Base::matchesFilter(c) &&
           ( ! getFilterInfo().getActiveContractsOnly() || c.isOpen() )
&&
           ( ! getFilterInfo().getMainSubscriberOnly() ||
             c.getClientCode() == getFilterInfo().getClientCode() ) &&
           ( ! getFilterInfo().getHideCorrespondenceAddress() ||
             c.getCorrAddr() == 0 ||
             c.getCorrAddr()->getAddress() ==
                    getFilterInfo().getClientAddress() ) &&
           ( ! getFilterInfo().getNullQuotesContracts() ||
             c.getQuotes() != 0 || ! c.isOpen() );
}
In questo caso la condizione e' strutturata in modo ricorrente, secondo
lo schema: o il flag di selezione non e' impostato oppure la condizione
e' verificata. Qui l'unica semplificazione che mi sentirei di fare e'
usare un reference per eliminare le chiamate ripetute a
getFilterInfo().
In genere poi indento le condizioni che vanno a capo di due livelli
senza curarmi ulteriormente del layout, ma in casi come questo
preferisco evidenziare piu' chiaramente la struttura dell'espressione.
Ciao,
Nicola

 getFilterInfo().
In quel caso userei qualcosa del genere:
bool P::matchesFilter(C & c) const
{
     return AND ( Base::matchesFilter(c),
                  OR ( ! getFilterInfo().getActiveContractsOnly(),
                       c.isOpen() ),
                  OR ( ! getFilterInfo().getMainSubscriberOnly(),
                       c.getClientCode() == 
getFilterInfo().getClientCode() ),
                  OR ( ! getFilterInfo().getHideCorrespondenceAddress(),
                       c.getCorrAddr() == 0,
                       c.getCorrAddr()->getAddress() == 
getFilterInfo().getClientAddress() ),
                  OR ( ! getFilterInfo().getNullQuotesContracts(),
                       c.getQuotes() != 0,
                       ! c.isOpen() ) );
}
L'indentazione verrà un po' persa nel post, ma dovresti ricostruirla in 
fretta. E' un modo di vedere la cosa da un punto di vista "più funzionale".
Kiuhnm

 fretta. E' un modo di vedere la cosa da un punto di vista "più funzionale".
Preferisco non inventare meccanismi nuovi per cose che il linguaggio
supporta adeguatamente.
Ciao,
Nicola

 supporta adeguatamente.
Infatti ritengo che in quel caso il supporto del linguaggio non sia 
affatto sufficiente. La seconda forma mi pare /nettamente/ più leggibile.
Kiuhnm


Secondo me e' meglio cercare di abituarsi ad usare ogni linguaggio in
modo idiomatico, piuttosto che tentare di trasformarlo in un altro
linguaggio, apparentemente piu' vicino ai propri gusti. Soprattutto se
si deve lavorare con altri.
Ciao,
Nicola

 si deve lavorare con altri.
Un linguaggio che non consente al programmatore di aggiungere astrazioni 
linguistiche č molto limitativo.
Ogni volta che usi una funzione non definita nello standard stai 
tagliando fuori una parte dei programmatori.
Il miglior codice non č quello che si capisce senza dover acquisire 
informazioni aggiuntive, ma quello che *complessivamente* si comprende e 
si gestisce nel minor tempo.
I programmatori imparano le astrazioni linguistiche in 10 minuti e poi 
comprendono e sviluppano il codice 10 volte piů velocemente.
Kiuhnm

 comprendono e sviluppano il codice 10 volte più velocemente.
Oppure sono cosi' spaventati da cio' che a loro appare fuori dalla
norma che non lo imparano proprio.
Ciao,
Nicola

Nicola Musatti wrote:
[...]
Sono sostanzialmente d'accordo con quanto hai detto.
Kiuhnm

 
Senza ombra di dubbio questo, io lo trovo molto piu' leggibile.
-- 
Roal Zanazzi

   r = 30;
io non vedo necessità di trasformarlo, per me va bene così: due casi 
possibili, due valori assegnati, uno per ciascun caso. Qui l'operatore 
ternario, sempre IMHO, renderebbe il codice meno leggibile mentre così è 
già molto chiaro...
Sarebbe bello sapere sempre esattamente, in ogni punto del codice, quali 
sono i valori che le variabili dovrebbero avere e quali invece NON 
dovrebbero avere... si potrebbe mettere un bell'ASSERT generale e 
beccare subito qualunque bug :-)

gli altri no, quindi sono superflui.
In realtà, il succo del discorso è sempre lo stesso:
(a==true) 
equivale a:
((a==true)==true)
che equivale a:
(((a==true)==true)==true)
e così via...

 comprendono e sviluppano il codice 10 volte piů velocemente.
Questa č una massima superficialmente vera e profondamente sbagliata :-)
Ogni astrazione sottintende una logica, un modo di pensare, un modo di 
"vedere" il codice: tu parti dal presupposto che il tuo modo di pensare 
sia universale, cioč condiviso da tutti, ma non č cosě. Un altro 
programmatore potrebbe trovarsi molto scomodo con la TUA serie di 
astrazioni e faticare tanto a capirla da decidere di cancellare e 
riscrivere intere parti del tuo codice.
P.S. tu solitamente non programmi in un team vero? :-)

 riscrivere intere parti del tuo codice.
Non condivido questo modo di pensare.
In un team si discute e si decide cosa fare.
Questo vale anche se il team è composto da una persona.
Ogniqualvolta si studia una nuova materia s'impara un nuovo linguaggio.
Quando studi analisi, algebra lineare, gli automi, ecc... sei costretto 
a imparare un nuovo linguaggio, e questo è giusto.
Il linguaggio base è sempre lo stesso, ma vengono costruite su di esso 
nuove astrazioni.
Per es.
Sum_{i=1}^n x_i := x_1+x_2+...+x_n.
s = Sum_{i=1}^{20} i^2
ecc...
L'autore non avrebbe dovuto introdurre la sommatoria? Chi vuole capire 
il suo lavoro dovrà studiarsi la sua nomenclatura, poiché quest'ultima 
gli ha permesso di risparmiare tempo prezioso e, soprattutto, di 
arrivare a risultati che altrimenti non avrebbe probabilmente raggiunto.
Se vuoi capire il codice scritto da un altro, allora dovrai imparare il 
suo linguaggio. Prendi in mano qualsiasi libro di matematica e vedrai 
che all'inizio ci sono una miriade di definizioni e simboli. Alcuni sono 
più o meno standard, mentre altri variano da materia a materia o da 
autore ad autore. Questo non ha mai impedito la comprensione dei 
contenuti. L'autore ha scelto certe astrazioni perché ha ritenuto che 
fossero utili. Prima si rende la vita più facile a sé stessi ed 
eventualmente anche agli altri.
Un programmatore che non è in grado di leggere le definizioni delle 
astrazioni, di capirle e di usarle, farà bene a stare lontano dal mio 
codice.
Kiuhnm

codice.
ROTFL.

 codice.
Sì... decisamente non programmi in un team :-)

 Sě... decisamente non programmi in un team :-)
E' una bella fortuna, se la pensano tutti come te.
Kiuhnm

 codice.
Non e' il principio che contesto, ma la scelta del punto di equilibrio.
Per esempio mi piace Spirit (
http://spirit.sourceforge.net/
), che trovo
un ragionevole compromesso tra il linguaggio del dominio (ovvero la
notazione EBNF) ed il C++, ma non reimplementerei le primitive
dell'algebra booleana, perche' le considero una componente fondamentale
del linguaggio che uso.
Ciao,
Nicola

 non sono gli altri quelli costretti a capire il tuo OR.
E' vero che il linguaggio per funzionare deve essere concordato tra
sorgente e destinazione; d'altra parte tale accordo puo' benissimo
avvenire tramite il capitolo di un libro, un corso propedeutico o anche
gli header fondamentali di un progetto.
Ciao,
Nicola

 del linguaggio che uso.
Se tu scrivi
AND(x_1,x_2,...,x_n) := x_1 && x_2 && ... && x_n
non hai reimplementato niente.
Hai semplicemente aggiunto una scorciatoia ("syntactic sugar", per alcuni).
Nessuna espressione precedentemente valida diventa invalida e nessuna 
espressione invalida diventa valida a meno che le parole AND e OR non 
siano in essa contenuta.
Kiuhnm

 codice meno leggibile mentre così è già molto chiaro...
Infatti, dipende del viso del codice. Se il viso è di fare un
scelto, è meglio utilisare il if. Se il viso è d'assegna un
valore a r, è meglio:
    r = f()
        ? 20
        : 30 ;
(In un caso così semplice, forse le tre linee sia un po`
essagerato. Mà in generale, trovo che la gente che normalemente
hanno problemi coll'operatore ?: capisconon quando lo scrivo
così.)
In ogni caso, devi mettere la cosa la più importante al nivello
il più visibile: l'if, se l'importante è la scelta, et l'=, se
l'importante è l'affettazione.
--
James Kanze                                           GABI Software
Conseils en informatique orientée objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, 
+33 (0)1 30 23 00 34





Ma anche Nicola ha rispetto l'indentazione. Infatti, la sola
differenza che vedo, è che il codice di Nicola si capisce di
ogni persona che conosce il C++, sibene che il tuo, solo di
quelli che conosconon anche i tui macros.
Infatti, faccio un po` diverse di Nicola, in cuì metto
l'operatore a l'inizio delle linee:
     return Base::matchesFilter(c)
        && ( ! getFilterInfo().getActiveContractsOnly()
            || c.isOpen() )
        && ( ! getFilterInfo().getMainSubscriberOnly()
            || c.getClientCode() == getFilterInfo().getClientCode() )
        && ( ! getFilterInfo().getHideCorrespondenceAddress()
            || c.getCorrAddr() == 0
            || c.getCorrAddr()->getAddress()
                    == getFilterInfo().getClientAddress() )
        && ( ! getFilterInfo().getNullQuotesContracts()
            || c.getQuotes() != 0
            || ! c.isOpen() );
(Anche, in generale metto le sotto-condizioni un po` complicate
in funzionni seperate. Mà dipende molto dell'applicazione.)


 siano in essa contenuta.
Inoltre non troverai mai un
if (OR(AND(a,b),c))
nel mio codice, però troverai alcuni
if (AND (....................,
          ....................,
          ....................,
          OR (........,
              ........,
              ........),
          ................
          .
          .
          ................)
Kiuhnm

 in funzionni seperate. Mà dipende molto dell'applicazione.)
IMHO, meno ridondante è il codice meglio è.
1/20 [[1,2,3],[2,3,4],[3,4,5]]
è meglio di
[[1/20,1/10,3/20], ecc...
Se mi trovo a scrivere
x_1+x_2+x_3+x_4 + ...
Passo a scrivere
Sum_i x_i
Il tuo codice è già molto leggibile.
Comunque ritengo che chi non capisce cosa fanno le macro AND e OR 
farebbero meglio a cambiare lavoro.
Lo dico anche perché è una comunissima tecnica espressiva usata in 
matematica (logica).
Qualsiasi operatore 'infix' può essere usato come 'prefix':
x_1 U x_2 U ... U x_n
equivale a (in LaTeX)
U_{i=1}^n x_i
x_1 and x_2 and ... and x_n
diventa
And_{i=1}^n x_i
ecc...
Kiuhnm

 gli header fondamentali di un progetto.
Ok e siamo d'accordo.  Che un linguaggio sia accordato tra più persone
significa proprio che non esiste un *mio* linguaggio al quale bisogna
necessariamente adeguarsi, quindi lavorando con altri non ci si può
trincerare dietro le proprie "astrazioni".
Poi, è realmente necessario inventare una nuova sintassi?  A me il C++
sembra già abbastanza espressivo.  Nel caso specifico, sostituire II
con un OR di quel tipo mi sembra qualcosa di cui si può fare benissimo
a meno.
D'altronde, non è un'astrazione che offra chissà quali vantaggi
interpretativi (anzi).
Sostituire:
a || b
con
OR(a, b)
mi sembra veramente, veramente orribile e sicuramente meno intuitivo...
Se devo stare lontano dal codice del tale signor K. (uso la modalità
kafkiana perché ha un nick inscrivibile), lo faccio volentieri ;)
Andrea Laforgia

E' una bella fortuna, se la pensano tutti come te.
Quanta presunzione questi "ricercatori" che non hanno mai scritto una
riga di codice ;-)

farebbero meglio a cambiare lavoro.
Chi usa ancora le macro farebbe bene a cambiare lavoro ;-)
A proposito, tu che lavoro fai ?

 kafkiana perché ha un nick inscrivibile)
beh, nemmeno io so quasi come si scrive o sillaba, ma faccio il 
giro esagonale dei tasti in senso antiorario. Così diventa facile.
ciao
Soviet_Mario


 Chi usa ancora le macro farebbe bene a cambiare lavoro ;-)
uhm .... dici che non esiste nessun contesto dove possano 
risolvere più problemi di quanti ne pongono ?
Con quali costrutti del linguaggio implementeresti i 
"banalissimi" (molto virgolettato, dato che li ritengo preziosi 
quanto insidiosi) costrutti # (stringizing) e ## (token pasting) 
del preprocessore ?
Non è una domanda retorica, se hai altri workaround agevoli, 
pure io abbandono volentieri le macro. Ora come ora mi servono a 
creare stringhe di testo, anche di errore, fortemente dipendenti 
dal contesto senza scrivere troppo a mano, e molto parametrizzate.
ciao
Soviet_Mario


usare dei goto.
Le macro andrebbero evitate.

risolvere più problemi di quanti ne pongono ?
No.  Nessun contesto.  Usa le funzioni inline e campa felice ;-)

Andrea Laforgia ha scritto:

si, ma non so come risolvere l'emulazione dei due contesti che 
avevo scritto (sono tagliati in questo post, ma cmq riguardavano 
lo "stringizing" con # e il "token pasting" con ##.
Questi task non so emularli con funzioni inline.
Accetto consiglio ovviamente
ciao
Soviet

 Le macro andrebbero evitate.
eh he he. Si questo l'hai già detto (e in parte concordo).
Ma prima di come emuleresti le due caratteristiche delle macro 
che ho ribadito nell'altro post. Io le trovo difficilissime da 
sostituire (e quando possibile, pagando overspace e overtime 
pazzeschi). Naturalmente non vale usare caratteristiche avanzate 
ancora non standard (e cmq molto poco efficienti a runtime, come 
l'autoispezione del codice e dei tipi che usano, che so, le 
System.Reflections di M$)
ciao
Soviet

avevo scritto[...]
I due contesti non sono ugualmente emulabili, perché, tolte di mezzo
le macro, non c'č piů bisogno di quei due contesti.

che ho ribadito nell'altro post. 
Non le emulerei, perché non ne avrei bisogno.

 }
Ovviamente secondo me la soluzione piu' leggibile e':
if(!(f(s)&&g(s)&&h(s))&&(a(),b(),1))return 0;
specialmente dopo averla indentata correttamente:
   if(
!(f(
s)    &&    g(s
)&&h(
      s))&&
   (a(),
b(),
1))
   return 
0;
Scherzi aparte e' incredibile quanto un post iniziale relativamente poco
interessante si sia trasformato in un thread enorme...
Tommaso
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

interessante si sia trasformato in un thread enorme...
Concordo.  Purtroppo le questioni "relativamente poco interessanti"
sono una specialitą del tale Kiuhnm... ;-)

Andrea Laforgia ha scritto:

beh, tra le strategie di risoluzione di un generico problema, di 
solito non è considerata una soluzione quella di cambiare la 
premessa, le ipotesi, che si assume vera, si chiede di 
dimostrare la tesi (nello specifico, trovare alternative 
altrettanto o più valide di dimostrazioni esistenti).
E' come dire che si fa una legge in modo che la malaria non 
debba più essere considerata una malattia, per cui non sono più 
necessarie ricerche sulle cure mediche necessarie.
Naturalmente si può fare anche questo, ma chi il problema lo ha, 
  deve risolverlo e stop.
ciao
cout << PASTE(Soviet##Mario)

premessa, 
Sei tu che stai impostando male il problema.  Nel contesto specifico,
se le macro sono la malaria e se utilizzo due tecniche particolari per
conviverci con questa malaria, una volta estirpata la malaria, ho
estirpato anche le due tecniche.

 se le macro sono la malaria 
no no ... è possibile che non mi sia spiegato bene. La malaria 
era un genere di problemi pratici per i quali quelle due macro 
erano la cura che ho già (funzionante). Ne cercavo una 
alternativa, tutto lì. L'unica cura che non mi serve è la 
strategia secondo cui i problemi che devo risolvere sono 
sbagliati e quindi non sussistono.
Con ciò, può anche essere che siano male impostati (anche se 
penso che non tutti lo siano).
Con questo chiudo la mia parte nell thread, dato che la 
discussione non riesce a assumere nessun connotato tecnico che 
potrebbe essere utile al NG.
ciao
Soviet_Mario


erano la cura che ho già (funzionante). 
Soviet_Mario, ti ostini a non voler capire che le macro sono qualcosa
di cui si può fare benissimo a meno e con esse tutto ciò che è ad esse
legato.

 Soviet_Mario, ti ostini a non voler capire 
uè .... no, non è che mi ostino. E' che ancora aspetto che mi 
spieghi come fare altrimenti quelle due cosette che ho chiesto 
in modo diverso.
ciao
Soviet


 spieghi come fare altrimenti
Sì, ti ostini.  Non hai capito, infatti, che quelle due "cosine" io
non le farei perché non mi servirebbe farle.

 usare dei goto.
Male. Le funzioni inline servono proprio a questo.
Ciao,
Nicola

 legato.
Se ti sentisse Paul Mensonides...
Secondo me sei troppo rigido. Esistono dei contesti, per altro
estremamente circoscritti, nei quali le macro servono eccome.
Ciao,
Nicola

 usata in matematica (logica).
E cosa mi dice che l'utilisazione qui corresponde a
l'utilisazione nelle matematica? Che AND non significa "adaptive
network discriminator", o qualcosa di genere?
La notazione di C++ non è quella della matematica. Per
incomminciare, la notazione di C++ si fà in una dimensionne,
quella della matematica in due, e qualcosa comme \Sigma_{i=1}^n
non e possibile in C++. Ma anche, perche molti programmi
trattono non solo della matematica, ma di altre cose, che hanno
anche il loro vocabulario.


 Hai semplicemente aggiunto una scorciatoia ("syntactic sugar", per alcuni).
E' che piu' che syntactic sugar a me pare syntactic castor oil... ;-)
Ciao,
Nicola Musatti




    #define LOG( level ) getLogStream( level, __FILE__, __LINE__ )
Come fai LOG comme funzioni inline?


 nuovo costrutto.
Questo ng non costituisce un gran campione, se permetti.
Soprattutto perché avete abitudini profondamente radicate e non per 
questo razionalmente migliori.
Basti pensare cosa pensano del C++ molti appartenenti alla comunità del C.
Kiuhnm

 network discriminator", o qualcosa di genere?
Se non riesci a capirlo dall'espressione, vai a cercarti la definizione.
Kiuhnm

 Scusi, ma senza verderle, comme sapere cosa fanno [le macro].
Lo specifico perché forse sono stato frainteso.
Mi riferivo ai programmatori di questo NG, nel contesto di questo 
thread, non al generico programmatore che legga il mio codice.
Kiuhnm


In parte e' certamente vero, d'altro canto il tuo esempio e' una
variante dello stesso problema posto su base individuale. Penso che
molti esperti lo porterebbero ad esempio di come *non* si deve
approcciare un nuovo linguaggio di programmazione.
Per quanto riguarda la comunita' del C immagino che tu ti riferisca a
quei personaggi che rifiutano il C++ per partito preso, per poi
scimmiottarlo in maniera incredibilmente macchinosa (vedi gtk/gnome, ad
esempio), o sbaglio?
Ciao,
Nicola

 Se non riesci a capirlo dall'espressione, vai a cercarti la definizione.
E' questo il nocciolo della questione: a || b || c lo capisci
direttamente dall'espressione senza dover cercare niente. Anzi, se c'e'
qualcosa da imparare in tutto questo e' che bisogna stare molto attenti
quando si fa overload di operatori.
Ciao,
Nicola

 esempio), o sbaglio?
Questo atteggiamento non mi è nuovo :)
Kiuhnm

 quando si fa overload di operatori.
Forse un giorno riuscirò a farti capire che esistono principalmente tre 
tipi di astrazioni:
- linguistica
- procedurale
- legata ai dati
Il non essere abituati a usare quelle del primo tipo è un vostro problema.
L'astrazione da me introdotta è inoltre riconducibile a un'astrazione 
procedurale, infatti puoi considerare and e or due normali funzioni.
Seguendo la tua logica dovremmo limitare l'uso delle funzioni al solo 
scopo di evitare duplicazione di codice.
Comunque è palese che non possiamo trovare un accordo. E' chiaro che 
entrambi abbiamo valide (o così pensiamo) ragioni per difendere la 
nostra posizione. E' giusto così dopo tutto. Rispetto la tua posizione 
pur non condividendola.
Kiuhnm

 "diverse", ma incontro qualche lieve resistenza :)
Dipende dalle idee. A volte attenersi alle "regole del gioco" mantiene
alta la produttivita' piu' del piegarle ai propri desideri.
Ciao,
Nicola

 pur non condividendola.
Su questo sono d'accordo con te :-)
Ciao,
Nicola

 A volte attenersi alle "regole del gioco" [...]
Tue o del linguaggio?
Kiuhnm

 l'appunto costituiscano espansioni del linguaggio.
and() e or() estendono il linguaggio.
Oppure è già possibile scrivere &&(a,b,c,d,e) e nessuno me l'ha detto?
Kiuhnm

 argomento.
E' comunque legato al discorso. Sappiamo che ogni persona rappresenta le 
informazioni apprese in modo diverso. Un linguaggio dove è possibile 
esprimere la stessa cosa in un solo modo è limitativo. Per es. si 
possono distinguere individui che prediligono la rappresentazione 
visiva, uditiva/uditivo digitale e cinestetica. Io sono il tipo uditivo 
digitale (suoni e parole). Non puoi pretendere che io e un'individuo di 
tipo visivo ci esprimiamo allo stesso modo. Sebbene un linguaggio di 
programmazione sia più rigido di un linguaggio naturale, è bene 
mantenere una certa libertà espressiva in modo che ognuno assecondi la 
propria natura.
Kiuhnm

 diabete...
La confusione sui modi in cui si può fare una cosa è uno dei problemi 
che incontrano i principianti quando affrontano per la prima volta il C.
A parte questo, il linguaggio in sè non ci perde e non ci guadagna, a 
livello di "potenza", se ci sono più modi di fare la stessa cosa o ce 
n'è uno solo; per cui personalmente preferisco che ci sia "un solo modo 
giusto" (e meno confusione).


Del linguaggio e della comunita' che vi sta intorno.
Ciao,
Nicola

 propria natura.
Questo e' un punto di vista rispettabilissimo per un artista. Un po'
meno per un professionista, dal quale ci si aspetta il massimo sforzo
per farsi comprendere dal suo pubblico.
Ciao,
Nicola


Il problema e' che l'unico vantaggio che portano e' ridurre il numero
di caratteri necessari per esprimere l'espressione in presenza di un
numero elevato di argomenti ed hanno lo svantaggio di essere meno
intuitivi dell'uso degli operatori nativi.
Penso che imparare ad usare un linguaggio in modo idiomatico sia molto
piu' importante che imparare ad implementarci costrutti propri di altri
linguaggi al solo scopo di ricrearsi un "utero". Alla fine se quello
che fai lo fai solo per te stesso va benissimo, ma allora si tratta di
autogratificazione e non di "best practices".
Ciao,
Nicola

 intuitivi dell'uso degli operatori nativi.
Niente di più falso.
"&&(" ti dà già un'idea di quello che seguirà.
Esistono principalmente due modi di organizzare le informazioni:
sequenziale e a layer.
"a && b && c && d" è sequenziale perché devi leggere l'intera 
espressione per sapere che si tratta di una serie di sottoespressioni 
legate da '&&'.
"&&(a,b,c,d)" è a layer perché l'informazione più generale è all'inizio 
dell'espressione.
L'idea è quella definire una struttura complessa in modo incrementale. 
Spesso è sufficiente fermarsi ai primi layer ignorando il resto. Usando 
gli operatori infix è invece necessario esaminare l'intera espressione.
Kiuhnm

 Del linguaggio e della comunita' che vi sta intorno.
Ovvero quello che dice la maggioranza è sempre giusto.
Kiuhnm


Non necessariamente, ma se si parla la sua lingua si ha miglior
probabilita' di essere compresi.
Ciao,
Nicola

 dell'espressione.
interessante classificazione. Non la conoscevo, ma trovo che 
esprima in modo rigoroso quello che avevo cercato di spiegare 
con un giro di parole macchinoso a proposito dello scorrimento 
di lunghe righe di testo.
Con questo non voglio discutere nel merito del fatto che il 
ritrovarsi con espressioni così lunghe da essere poco leggibili 
sia indice di cattivo design (dato che questo è possibile).
In generale, al di fuori di un contesto specifico, la notazione 
prefissa mi piace se l'espressione è molto ripetitiva.
Molti prefissi accatastati all'inizio, invece, non mi aiutano di 
molto a parserizzare, perchè devo comunque scorrere il testo 
sino a trovare le matching ( e ) .... a meno di non formattare 
multiriga, cosa che col mio monitor è scomoda
Ma tu che mestiere o che studi (o entrambi) stai facendo, per 
curiosità ? Sei un matematico ?
Ciao
Soviet


 gli operatori infix è invece necessario esaminare l'intera espressione.
Continua a non valerne la pena. La *vera* organizzazione a layer sta
nell'astrarre concetti da un livello all'altro, il che corrisponde a
introdurre funzioni con significato piu' specifico degli operatori
booleani elementari, da operatori compositi (ad es. "implica") a
funzioni specifiche di un dominio o un'applicazione (ad es. la mia
"matchesFilter").
Ciao,
Nicola

 ? Sei un matematico ?
Mi occupo di linguaggi (automi, compilatori, ecc...) trattati dal punto 
di vista algebrico.
Kiuhnm

 "matchesFilter").
Esistono vari livelli. Soltanto le astrazioni semplici e generiche 
vengono aggiunte direttamente al linguaggio.
Ecco un esempio classico.
Si assuma di aver un kernel language che supporti il seguente costrutto:
local <var> in <epxr> end
In breve, 'local' alloca una variabile locale var da usare in expr.
La prima astrazione linguistica introdotta è la seguente:
local <var1> ... <varn> in <expr> end
:=
local <var1> in
   ...
     local <varn> in <expr> end ... end
La mia astrazione viene invece introdotta solitamente in due passaggi:
foldl op {e1 e2 ... en} z :=
   {(...((z op e1) op e2) op ... op en)}
op(e1 e2 ... en) := foldl op {e1 e2 ... en}.
Le astrazioni linguistiche atte ad estendere il linguaggio di base 
devono essere /semplici/ e /graduali/. Solo così sono consistenti e coese.
Per es. il passo è breve da
&&(a,b,c,d)
a
&&(list)
dove list è un vector, una list, o altro.
Questo utilizzo dei kernel language è abbastanza recente, comunque.
Kiuhnm

 dove list è un vector, una list, o altro.
In un dialetto del Lisp non avrei alcuna obiezione.
Ciao,
Nicola

 In un dialetto del Lisp non avrei alcuna obiezione.
Non si sta parlando di programmazione funzionale, ma multiparadigmica.
Queste idee sono state introdotte anche in C++, sebbene, a causa delle 
limitazioni del linguaggio (closures, currying, ecc...), siano di scarsa 
utilitą.
Kiuhnm

 utilità.
Un conto e' ragionare su estensioni offuscanti, un conto su paradigmi.
Quello che intendevo dire e' che in un linguaggio come Scheme
l'introduzione di estensioni sintattiche e' un approccio, magari
avanzato, ma appartenente alla consuetudine. In C++ lo e' di meno. In
piu' bisogna vedere dove incidono queste estensioni: in generale
cercare di sostituire costrutti basilari di un linguaggio con altri
"inventati" non e' una buona idea; perche' una tale operazione sia
giustificata deve avere motivazioni a prova di bomba.
Invece per migliorare l'attitudine del C++ al paradigma funzionale ci
sono a mio avviso interventi meno discutibili e piu' efficaci che si
possono compiere. Ad esempio il mio codice ha preso un aspetto piu'
funzionale da quando ho introdotto l'uso di iteratori opachi per
passare in giro collezioni di oggetti. Questi, insieme a bind e
function_output_iterator di Boost e agli algoritmi della libreria
standard rendono spesso piu' pratico tale approccio rispetto alla
scrittura esplicita dei cicli.
Ciao,
Nicola

 passare in giro collezioni di oggetti.
Già che ci sei, puoi dirmi cosa sono questi "iteratori opachi"?
Kiuhnm

 Già che ci sei, puoi dirmi cosa sono questi "iteratori opachi"?
In effetti e' un termine appena inventato perche' se ce n'e' uno di uso
diffuso non lo conosco. Il problema di partenza e' come fornire
sequenze di oggetti omogenei nascondendo qualsiasi dettaglio su come
sono gestiti dal fornitore. Supponi di avere
class Oggetto;
class Fornitore {
  public:
    <da definire> oggetti(<da definire>);
  private:
    std::vector<Oggetto *> v;
};
La strada piu' semplice e' far si che Fornitore::oggetti() restituisca
un paio di iteratori:
std::pair<std::vector<Oggetto *>::iterator, std::vector<Oggetto
*>::iterator> oggetti() {
  return std::make_pair(v.begin(), v.end());
}
Ma questo smaschera il fatto che gli oggetti sono contenuti in un
vettore ed in piu' crea una dipendenza nascosta tra il lifetime del
fornitore e quello del cliente.
In alternativa si puo' pretendere che Fornitore::oggetti() riceva come
argomento un reference ad un contenitore, ma questo vincola la scelta
del buffer col quale effettuare lo scambio. E' un po meglio se
Fornitore::oggetti() diventa una funzione membro template sul tipo del
contenitore passato, ma nel mio caso facilmente queste funzioni membro
si trovano sull'interfaccia di una libreria dinamica e l'uso di un
simile template causerebbe una sorta di "straripamento" del codice
della libreria verso l'esterno.
La soluzione che ho trovato e' la realizzazione di un output iterator
generalizzato che ne prende uno specifico come argomento in fase di
creazione, ma maschera il meccanismo di delega verso quest'ultimo; il
risultato e' un output iterator che dipende esplicitamente solo dal
tipo dell'elemento indirizzato. La mia funzione membro
Fornitore::oggetti() e' quindi definita come
Fornitore::oggetti(OutputIter<Oggetto *> oi) {
  std::copy(v.begin(), v.end(), oi);
}
Mentre OutputIter usa l'idioma "Handle/Body" per "cancellare" il tipo
dell'iteratore contenuto.
In questo modo evito dipendenze perniciose tra fornitore e cliente sia
a livello di lifetime che di codice sorgente.
Ciao,
Nicola

 dell'iteratore contenuto.
OutputIter invia i dati direttamente a un iteratore fornito dal client?
Kiuhnm


Si. OutputIter ha un costruttore template che prende come argomento un
iteratore e ci costruisce intorno un wrapper al quale si accede via
funzioni membro virtuali.
Avanzare e dereferenziare un OutputIterator corrisponde ad avanzare e
dereferenziare l'iteratore contenuto. L'assegnamento all'oggetto
referenziato richiede invece l'uso di una classe proxy per gestire
l'eventuale incremento postfisso.
Ciao,
Nicola

 funzioni membro virtuali.
Le possibilità erano:
1) passare a Fornitore un iteratore di destinazione,
2) passare a Fornitore un container di destinazione,
3) ricevere da Fornitore un container sorgente,
4) ricevere da Fornitore due iteratori sorgente.
Non volendo usare i template, sei stato costretto a fare uso di una 
classe base. Non credo che esista un altro modo con le restrizioni che 
hai posto. Come ben sai, le STL usano gli iteratori proprio per 
genericità, però si affidano ai template.
Hai praticamente fatto quello che gli autori delle STL avrebbero fatto 
in mancanza dei template.
Riesco a pensare solo a un altro metodo. Non lo consiglio, ma, volendo, 
esiste.
Il consumatore non deve fare altro che creare degli oggetti "vuoti" e 
utilizzare lo spazio inutilizzato per costruire una lista linkata 
semplice che li leghi.
Alla funzione oggetti() passerà soltanto il puntatore al primo oggetto.
Ovviamente è necessario sapere in anticipo quanti sono gli oggetti.
Se l'inserimento di ogni oggetto dipende dal suo contenuto (come in 
std::map, per esempio), è chiaro che bisognerà inserirli dopo che 
Fornitore avrà finito di "inizializzarli" (in tal caso la lista linkata 
dovrà essere esterna ad essi).
Comunque (algoritmicamente parlando) è più efficiente ordinare degli 
oggetti e creare una mappa piuttosto che inserire gli oggetti a uno a 
uno direttamente nella mappa.
Kiuhnm

 in mancanza dei template.
Beh, comunque il mio OutputIter e' un template, solo che espone
esclusivamente il tipo dell'oggetto riferito e non i propri dettagli di
funzionamento.
Quello che c'era prima di STL e' una cosa che se non sbaglio si
chiamava generic.h, completamente basata su macro e la cui bruttezza
penso sia stata all'origine dell'invenzione dei template; credo che
James Kanze ne sappia molto piu' di me.
Ciao,
Nicola

 funzioni membro virtuali.
Tempo fa scrissi qualcosa di sostanzialmente analogo:
 
http://proxy.maxim.comm2000.it/~maxim/pub/uiterator.hxx
che permette, appunto, di definire degli iteratori (forward) il cui tipo
dipende esclusivamente dal tipo degli oggetti e non dal container.
Max

 James Kanze ne sappia molto piu' di me.
Da ex-schiavo dell'efficienza, avrei dovuto sospettarlo...
Kiuhnm